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."));
4786 g_object_set (s_con,
4787 NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
4793 master = master_ask = nmc_readline (PROMPT_MASTER);
4795 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4796 _("Error: 'master' is required."));
4799 /* Verify master argument */
4800 checked_master = normalized_master_for_slave (all_connections, master, slave_type, NULL);
4802 /* Change properties in 'connection' setting */
4803 g_object_set (s_con,
4804 NM_SETTING_CONNECTION_MASTER, checked_master,
4805 NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
4808 g_free (master_ask);
4814 complete_connection_by_type (NMConnection *connection,
4815 const char *con_type,
4816 const GPtrArray *all_connections,
4818 gboolean show_secrets,
4823 NMSettingConnection *s_con;
4824 NMSettingGeneric *s_generic;
4825 NMSettingWired *s_wired;
4826 NMSettingInfiniband *s_infiniband;
4827 NMSettingWireless *s_wifi;
4828 NMSettingWimax *s_wimax;
4829 NMSettingPppoe *s_pppoe;
4830 NMSettingGsm *s_gsm;
4831 NMSettingCdma *s_cdma;
4832 NMSettingBluetooth *s_bt;
4833 NMSettingVlan *s_vlan;
4834 NMSettingBond *s_bond;
4835 NMSettingTeam *s_team;
4836 NMSettingTeamPort *s_team_port;
4837 NMSettingBridge *s_bridge;
4838 NMSettingBridgePort *s_bridge_port;
4839 NMSettingVpn *s_vpn;
4840 NMSettingOlpcMesh *s_olpc_mesh;
4841 NMSettingAdsl *s_adsl;
4842 NMSettingTun *s_tun;
4843 NMSettingIPTunnel *s_ip_tunnel;
4844 NMSettingMacvlan *s_macvlan;
4845 NMSettingVxlan *s_vxlan;
4847 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4849 s_con = nm_connection_get_setting_connection (connection);
4852 if (!strcmp (con_type, NM_SETTING_WIRED_SETTING_NAME)) {
4853 /* Build up the settings required for 'ethernet' */
4854 gboolean success = FALSE;
4855 const char *mtu_c = NULL;
4857 guint32 mtu_int = 0;
4858 const char *mac_c = NULL;
4860 const char *cloned_mac_c = NULL;
4861 char *cloned_mac = NULL;
4862 nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE},
4863 {"mac", TRUE, &mac_c, FALSE},
4864 {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
4867 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4870 /* Also ask for all optional arguments if '--ask' is specified. */
4871 mtu = g_strdup (mtu_c);
4872 mac = g_strdup (mac_c);
4873 cloned_mac = g_strdup (cloned_mac_c);
4875 do_questionnaire_ethernet (TRUE, &mtu, &mac, &cloned_mac);
4877 if (!check_and_convert_mtu (mtu, &mtu_int, error))
4879 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
4881 if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error))
4884 /* Add ethernet setting */
4885 s_wired = (NMSettingWired *) nm_setting_wired_new ();
4886 nm_connection_add_setting (connection, NM_SETTING (s_wired));
4889 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
4891 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
4893 g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_mac, NULL);
4899 g_free (cloned_mac);
4903 } else if (!strcmp (con_type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
4904 /* Build up the settings required for 'infiniband' */
4905 gboolean success = FALSE;
4906 const char *mtu_c = NULL;
4908 guint32 mtu_int = 0;
4909 const char *mac_c = NULL;
4911 const char *mode_c = NULL;
4913 const char *parent_c = NULL;
4914 char *parent = NULL;
4915 const char *p_key_c = NULL;
4917 guint32 p_key_int = 0;
4918 nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE},
4919 {"mac", TRUE, &mac_c, FALSE},
4920 {"transport-mode", TRUE, &mode_c, FALSE},
4921 {"parent", TRUE, &parent_c, FALSE},
4922 {"p-key", TRUE, &p_key_c, FALSE},
4925 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4928 /* Also ask for all optional arguments if '--ask' is specified. */
4929 mtu = g_strdup (mtu_c);
4930 mac = g_strdup (mac_c);
4931 mode = g_strdup (mode_c);
4932 parent = g_strdup (parent_c);
4933 p_key = g_strdup (p_key_c);
4935 do_questionnaire_infiniband (&mtu, &mac, &mode, &parent, &p_key);
4937 if (!check_and_convert_mtu (mtu, &mtu_int, error))
4939 if (!check_mac (mac, ARPHRD_INFINIBAND, "mac", error))
4941 if (!check_infiniband_mode (&mode, error))
4944 if (!check_infiniband_p_key (p_key, &p_key_int, error))
4946 if (!check_infiniband_parent (parent, error))
4948 } else if (parent) {
4949 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4950 _("Error: 'parent': not valid without 'p-key'."));
4954 /* Add 'infiniband' setting */
4955 s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new ();
4956 nm_connection_add_setting (connection, NM_SETTING (s_infiniband));
4958 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, mode ? mode : "datagram", NULL);
4960 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu_int, NULL);
4962 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL);
4964 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_P_KEY, p_key_int, NULL);
4966 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_PARENT, parent, NULL);
4979 } else if (!strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME)) {
4980 /* Build up the settings required for 'wifi' */
4981 gboolean success = FALSE;
4982 char *ssid_ask = NULL;
4983 const char *ssid = NULL;
4985 const char *mtu_c = NULL;
4987 guint32 mtu_int = 0;
4988 const char *mac_c = NULL;
4990 const char *cloned_mac_c = NULL;
4991 char *cloned_mac = NULL;
4992 const char *mode_c = NULL;
4994 nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask},
4995 {"mtu", TRUE, &mtu_c, FALSE},
4996 {"mac", TRUE, &mac_c, FALSE},
4997 {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
4998 {"mode", TRUE, &mode_c, FALSE},
5001 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5005 ssid = ssid_ask = nmc_readline (_("SSID: "));
5007 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5008 _("Error: 'ssid' is required."));
5012 /* Also ask for all optional arguments if '--ask' is specified. */
5013 mtu = g_strdup (mtu_c);
5014 mac = g_strdup (mac_c);
5015 cloned_mac = g_strdup (cloned_mac_c);
5016 mode = g_strdup (mode_c);
5018 do_questionnaire_wifi (&mtu, &mac, &cloned_mac, &mode);
5020 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5022 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5024 if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error))
5026 if (!check_wifi_mode (&mode, error))
5029 /* Add wifi setting */
5030 s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
5031 nm_connection_add_setting (connection, NM_SETTING (s_wifi));
5033 ssid_bytes = g_bytes_new (ssid, strlen (ssid));
5034 g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid_bytes, NULL);
5037 g_object_set (s_wifi, NM_SETTING_WIRELESS_MTU, mtu_int, NULL);
5039 g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL);
5041 g_object_set (s_wifi, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, cloned_mac, NULL);
5043 g_object_set (s_wifi, NM_SETTING_WIRELESS_MODE, mode, NULL);
5045 g_bytes_unref (ssid_bytes);
5052 g_free (cloned_mac);
5057 } else if (!strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)) {
5058 /* Build up the settings required for 'wimax' */
5059 gboolean success = FALSE;
5060 const char *nsp_name = NULL;
5061 char *nsp_name_ask = NULL;
5062 const char *mac_c = NULL;
5064 nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !ask},
5065 {"mac", TRUE, &mac_c, FALSE},
5068 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5071 if (!nsp_name && ask)
5072 nsp_name = nsp_name_ask = nmc_readline (_("WiMAX NSP name: "));
5074 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5075 _("Error: 'nsp' is required."));
5079 /* Also ask for all optional arguments if '--ask' is specified. */
5080 mac = g_strdup (mac_c);
5082 do_questionnaire_wimax (&mac);
5084 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5087 /* Add 'wimax' setting */
5088 s_wimax = (NMSettingWimax *) nm_setting_wimax_new ();
5089 nm_connection_add_setting (connection, NM_SETTING (s_wimax));
5090 g_object_set (s_wimax, NM_SETTING_WIMAX_NETWORK_NAME, nsp_name, NULL);
5093 g_object_set (s_wimax, NM_SETTING_WIMAX_MAC_ADDRESS, mac, NULL);
5097 g_free (nsp_name_ask);
5102 } else if (!strcmp (con_type, NM_SETTING_PPPOE_SETTING_NAME)) {
5103 /* Build up the settings required for 'pppoe' */
5104 gboolean success = FALSE;
5105 const char *username = NULL;
5106 char *username_ask = NULL;
5107 const char *password_c = NULL;
5108 char *password = NULL;
5109 const char *service_c = NULL;
5110 char *service = NULL;
5111 const char *mtu_c = NULL;
5113 guint32 mtu_int = 0;
5114 const char *mac_c = NULL;
5116 nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask},
5117 {"password", TRUE, &password_c, FALSE},
5118 {"service", TRUE, &service_c, FALSE},
5119 {"mtu", TRUE, &mtu_c, FALSE},
5120 {"mac", TRUE, &mac_c, FALSE},
5123 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5126 if (!username && ask)
5127 username = username_ask = nmc_readline (_("PPPoE username: "));
5129 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5130 _("Error: 'username' is required."));
5134 /* Also ask for all optional arguments if '--ask' is specified. */
5135 password = g_strdup (password_c);
5136 service = g_strdup (service_c);
5137 mtu = g_strdup (mtu_c);
5138 mac = g_strdup (mac_c);
5140 do_questionnaire_pppoe (show_secrets, &password, &service, &mtu, &mac);
5142 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5144 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5147 /* Add 'pppoe' setting */
5148 s_pppoe = (NMSettingPppoe *) nm_setting_pppoe_new ();
5149 nm_connection_add_setting (connection, NM_SETTING (s_pppoe));
5150 g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, username, NULL);
5151 g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, password, NULL);
5152 g_object_set (s_pppoe, NM_SETTING_PPPOE_SERVICE, service, NULL);
5154 /* Add ethernet setting */
5155 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5156 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5158 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
5160 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
5164 g_free (username_ask);
5172 } else if ( !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME)
5173 || !strcmp (con_type, NM_SETTING_CDMA_SETTING_NAME)) {
5174 /* Build up the settings required for 'gsm' or 'cdma' mobile broadband */
5175 gboolean success = FALSE;
5176 const char *apn = NULL;
5177 char *apn_ask = NULL;
5178 const char *user_c = NULL;
5180 const char *password_c = NULL;
5181 char *password = NULL;
5184 nmc_arg_t gsm_args[] = { {NULL}, {NULL}, {NULL}, /* placeholders */
5187 is_gsm = !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME);
5190 gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !ask};
5191 gsm_args[i++] = (nmc_arg_t) {"user", TRUE, &user_c, FALSE};
5192 gsm_args[i++] = (nmc_arg_t) {"password", TRUE, &password_c, FALSE};
5193 gsm_args[i++] = (nmc_arg_t) {NULL};
5195 if (!nmc_parse_args (gsm_args, FALSE, &argc, &argv, error))
5198 if (!apn && ask && is_gsm)
5199 apn = apn_ask = nmc_readline (_("APN: "));
5200 if (!apn && is_gsm) {
5201 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5202 _("Error: 'apn' is required."));
5203 goto cleanup_mobile;
5206 /* Also ask for all optional arguments if '--ask' is specified. */
5207 user = g_strdup (user_c);
5208 password = g_strdup (password_c);
5210 do_questionnaire_mobile (show_secrets, &user, &password);
5213 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, NULL);
5215 /* Add 'gsm' setting */
5216 s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
5217 nm_connection_add_setting (connection, NM_SETTING (s_gsm));
5218 g_object_set (s_gsm,
5219 NM_SETTING_GSM_NUMBER, "*99#",
5220 NM_SETTING_GSM_APN, apn,
5221 NM_SETTING_GSM_USERNAME, user,
5222 NM_SETTING_GSM_PASSWORD, password,
5226 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME, NULL);
5228 /* Add 'cdma' setting */
5229 s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
5230 nm_connection_add_setting (connection, NM_SETTING (s_cdma));
5231 g_object_set (s_cdma,
5232 NM_SETTING_CDMA_NUMBER, "#777",
5233 NM_SETTING_CDMA_USERNAME, user,
5234 NM_SETTING_CDMA_PASSWORD, password,
5245 } else if (!strcmp (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
5246 /* Build up the settings required for 'bluetooth' */
5247 gboolean success = FALSE;
5248 const char *addr = NULL;
5249 char *addr_ask = NULL;
5250 const char *bt_type_c = NULL;
5251 char *bt_type = NULL;
5252 nmc_arg_t exp_args[] = { {"addr", TRUE, &addr, !ask},
5253 {"bt-type", TRUE, &bt_type_c, FALSE},
5256 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5260 addr = addr_ask = nmc_readline (_("Bluetooth device address: "));
5262 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5263 _("Error: 'addr' is required."));
5266 if (!check_mac (addr, ARPHRD_ETHER, "addr", error))
5269 /* Also ask for all optional arguments if '--ask' is specified. */
5270 bt_type = g_strdup (bt_type_c);
5272 do_questionnaire_bluetooth (&bt_type);
5274 /* Default to 'panu' if bt-type is not provided. */
5276 bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_PANU);
5278 /* Add 'bluetooth' setting */
5279 s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new ();
5280 nm_connection_add_setting (connection, NM_SETTING (s_bt));
5283 g_object_set (s_bt, NM_SETTING_BLUETOOTH_BDADDR, addr, NULL);
5285 /* 'dun' type requires adding 'gsm' or 'cdma' setting */
5286 if ( !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
5287 || !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) {
5288 bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
5289 s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
5290 nm_connection_add_setting (connection, NM_SETTING (s_gsm));
5291 g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
5292 // g_object_set (s_gsm, NM_SETTING_GSM_APN, "FIXME", NULL;
5294 } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) {
5295 bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
5296 s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
5297 nm_connection_add_setting (connection, NM_SETTING (s_cdma));
5298 g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL);
5300 } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) {
5303 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5304 _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."),
5305 bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN,
5306 NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma");
5309 g_object_set (s_bt, NM_SETTING_BLUETOOTH_TYPE, bt_type, NULL);
5318 } else if (!strcmp (con_type, NM_SETTING_VLAN_SETTING_NAME)) {
5319 /* Build up the settings required for 'vlan' */
5320 gboolean success = FALSE;
5321 const char *parent = NULL;
5322 char *parent_ask = NULL;
5323 const char *vlan_id = NULL;
5324 char *vlan_id_ask = NULL;
5325 unsigned long id = 0;
5326 const char *flags_c = NULL;
5328 guint32 flags_int = 0;
5329 const char *ingress_c = NULL, *egress_c = NULL;
5330 char *ingress = NULL, *egress = NULL;
5331 char **ingress_arr = NULL, **egress_arr = NULL, **p;
5332 const char *mtu_c = NULL;
5335 gboolean valid_mac = FALSE;
5336 nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask},
5337 {"id", TRUE, &vlan_id, !ask},
5338 {"flags", TRUE, &flags_c, FALSE},
5339 {"ingress", TRUE, &ingress_c, FALSE},
5340 {"egress", TRUE, &egress_c, FALSE},
5341 {"mtu", TRUE, &mtu_c, FALSE},
5344 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5348 parent = parent_ask = nmc_readline (_("VLAN parent device or connection UUID: "));
5350 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5351 _("Error: 'dev' is required."));
5354 if (!vlan_id && ask)
5355 vlan_id = vlan_id_ask = nmc_readline (_("VLAN ID <0-4094>: "));
5357 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5358 _("Error: 'id' is required."));
5362 if (!nmc_string_to_uint (vlan_id, TRUE, 0, 4094, &id)) {
5363 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5364 _("Error: 'id': '%s' is not valid; use <0-4094>."),
5370 if ( !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN))
5371 && !nm_utils_is_uuid (parent)
5372 && !nm_utils_iface_valid_name (parent)) {
5373 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5374 _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
5379 /* Also ask for all optional arguments if '--ask' is specified. */
5380 mtu = g_strdup (mtu_c);
5381 flags = g_strdup (flags_c);
5382 ingress = g_strdup (ingress_c);
5383 egress = g_strdup (egress_c);
5385 do_questionnaire_vlan (&mtu, &flags, &ingress, &egress);
5387 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5389 if (!check_and_convert_vlan_flags (flags, &flags_int, error))
5391 if (!check_and_convert_vlan_prio_maps (ingress, NM_VLAN_INGRESS_MAP, &ingress_arr, error))
5393 if (!check_and_convert_vlan_prio_maps (egress, NM_VLAN_EGRESS_MAP, &egress_arr, error))
5396 /* Add 'vlan' setting */
5397 s_vlan = (NMSettingVlan *) nm_setting_vlan_new ();
5398 nm_connection_add_setting (connection, NM_SETTING (s_vlan));
5400 /* Add 'wired' setting if necessary */
5401 if (mtu || valid_mac) {
5402 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5403 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5406 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
5408 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL);
5411 /* Set 'vlan' properties */
5413 g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL);
5415 g_object_set (s_vlan, NM_SETTING_VLAN_ID, id, NULL);
5418 g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, flags_int, NULL);
5419 for (p = ingress_arr; p && *p; p++)
5420 nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, *p);
5421 for (p = egress_arr; p && *p; p++)
5422 nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, *p);
5430 g_free (parent_ask);
5431 g_free (vlan_id_ask);
5432 g_strfreev (ingress_arr);
5433 g_strfreev (egress_arr);
5437 } else if (!strcmp (con_type, NM_SETTING_BOND_SETTING_NAME)) {
5438 /* Build up the settings required for 'bond' */
5439 gboolean success = FALSE;
5440 const char *ifname = NULL;
5441 const char *bond_mode_c = NULL;
5442 char *bond_mode = NULL;
5443 const char *bond_primary_c = NULL;
5444 char *bond_primary = NULL;
5445 const char *bond_miimon_c = NULL;
5446 char *bond_miimon = NULL;
5447 const char *bond_downdelay_c = NULL;
5448 char *bond_downdelay = NULL;
5449 const char *bond_updelay_c = NULL;
5450 char *bond_updelay = NULL;
5451 const char *bond_arpinterval_c = NULL;
5452 char *bond_arpinterval = NULL;
5453 const char *bond_arpiptarget_c = NULL;
5454 char *bond_arpiptarget = NULL;
5455 const char *bond_lacp_rate_c = NULL;
5456 char *bond_lacp_rate = NULL;
5457 nmc_arg_t exp_args[] = { {"mode", TRUE, &bond_mode_c, FALSE},
5458 {"primary", TRUE, &bond_primary_c, FALSE},
5459 {"miimon", TRUE, &bond_miimon_c, FALSE},
5460 {"downdelay", TRUE, &bond_downdelay_c, FALSE},
5461 {"updelay", TRUE, &bond_updelay_c, FALSE},
5462 {"arp-interval", TRUE, &bond_arpinterval_c, FALSE},
5463 {"arp-ip-target", TRUE, &bond_arpiptarget_c, FALSE},
5464 {"lacp-rate", TRUE, &bond_lacp_rate_c, FALSE},
5467 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5470 /* Also ask for all optional arguments if '--ask' is specified. */
5471 bond_mode = g_strdup (bond_mode_c);
5472 bond_primary = g_strdup (bond_primary_c);
5473 bond_miimon = g_strdup (bond_miimon_c);
5474 bond_downdelay = g_strdup (bond_downdelay_c);
5475 bond_updelay = g_strdup (bond_updelay_c);
5476 bond_arpinterval = g_strdup (bond_arpinterval_c);
5477 bond_arpiptarget = g_strdup (bond_arpiptarget_c);
5478 bond_lacp_rate = g_strdup (bond_lacp_rate_c);
5480 do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon,
5481 &bond_downdelay, &bond_updelay,
5482 &bond_arpinterval, &bond_arpiptarget,
5485 /* Generate ifname if connection doesn't have one */
5486 ifname = nm_setting_connection_get_interface_name (s_con);
5488 char *bond_ifname = unique_master_iface_ifname (all_connections, "nm-bond");
5490 g_object_set (s_con,
5491 NM_SETTING_CONNECTION_INTERFACE_NAME, bond_ifname,
5493 g_free (bond_ifname);
5496 /* Add 'bond' setting */
5497 s_bond = (NMSettingBond *) nm_setting_bond_new ();
5498 nm_connection_add_setting (connection, NM_SETTING (s_bond));
5500 /* Set bond options */
5504 if (!(bm = nmc_bond_validate_mode (bond_mode, &err))) {
5505 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5506 _("Error: 'mode': %s."), err->message);
5507 g_clear_error (&err);
5510 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, bm);
5513 if (!nm_utils_iface_valid_name (bond_primary)) {
5514 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5515 _("Error: 'primary': '%s' is not a valid interface name."),
5519 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, bond_primary);
5522 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, bond_miimon);
5523 if (bond_downdelay && strcmp (bond_downdelay, "0") != 0)
5524 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, bond_downdelay);
5525 if (bond_updelay && strcmp (bond_updelay, "0") != 0)
5526 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, bond_updelay);
5527 if (bond_arpinterval && strcmp (bond_arpinterval, "0") != 0)
5528 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, bond_arpinterval);
5529 if (bond_arpiptarget)
5530 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, bond_arpiptarget);
5532 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_LACP_RATE, bond_lacp_rate);
5537 g_free (bond_primary);
5538 g_free (bond_miimon);
5539 g_free (bond_downdelay);
5540 g_free (bond_updelay);
5541 g_free (bond_arpinterval);
5542 g_free (bond_arpiptarget);
5543 g_free (bond_lacp_rate);
5547 } else if (!strcmp (con_type, "bond-slave")) {
5548 /* Slave types without any specific settings ('bond-slave') */
5549 const char *master = NULL;
5550 const char *type = NULL;
5551 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
5552 {"type", TRUE, &type, FALSE},
5555 /* Set global variables for use in TAB completion */
5556 nmc_tab_completion.con_type = NM_SETTING_BOND_SETTING_NAME;
5558 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5561 if (!complete_slave (s_con, all_connections, NM_SETTING_BOND_SETTING_NAME, master, type, ask, error))
5564 /* Change properties in 'connection' setting */
5565 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NULL);
5567 /* Add ethernet setting */
5568 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5569 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5571 } else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) {
5572 /* Build up the settings required for 'team' */
5573 gboolean success = FALSE;
5574 const char *ifname = NULL;
5575 const char *config_c = NULL;
5576 char *config = NULL;
5578 nmc_arg_t exp_args[] = { {"config", TRUE, &config_c, FALSE},
5581 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5584 /* Also ask for all optional arguments if '--ask' is specified. */
5585 config = g_strdup (config_c);
5587 do_questionnaire_team (&config);
5589 /* Generate ifname if conneciton doesn't have one */
5590 ifname = nm_setting_connection_get_interface_name (s_con);
5592 char *team_ifname = unique_master_iface_ifname (all_connections, "nm-team");
5594 g_object_set (s_con,
5595 NM_SETTING_CONNECTION_INTERFACE_NAME, team_ifname,
5597 g_free (team_ifname);
5600 /* Add 'team' setting */
5601 s_team = (NMSettingTeam *) nm_setting_team_new ();
5602 nm_connection_add_setting (connection, NM_SETTING (s_team));
5604 if (!nmc_team_check_config (config, &json, error)) {
5605 g_prefix_error (error, _("Error: "));
5609 /* Set team options */
5610 g_object_set (s_team, NM_SETTING_TEAM_CONFIG, json, NULL);
5619 } else if (!strcmp (con_type, "team-slave")) {
5620 /* Build up the settings required for 'team-slave' */
5621 gboolean success = FALSE;
5622 const char *master = NULL;
5623 char *master_ask = NULL;
5624 const char *type = NULL;
5625 const char *config_c = NULL;
5626 char *config = NULL;
5628 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
5629 {"type", TRUE, &type, FALSE},
5630 {"config", TRUE, &config_c, FALSE},
5633 /* Set global variables for use in TAB completion */
5634 nmc_tab_completion.con_type = NM_SETTING_TEAM_SETTING_NAME;
5636 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5639 if (!complete_slave (s_con, all_connections, NM_SETTING_TEAM_SETTING_NAME, master, type, ask, error))
5642 /* Also ask for all optional arguments if '--ask' is specified. */
5643 config = g_strdup (config_c);
5645 do_questionnaire_team_slave (&config);
5647 /* Add 'team-port' setting */
5648 s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new ();
5649 nm_connection_add_setting (connection, NM_SETTING (s_team_port));
5651 if (!nmc_team_check_config (config, &json, error)) {
5652 g_prefix_error (error, _("Error: "));
5653 goto cleanup_team_slave;
5656 /* Set team-port options */
5657 g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, json, NULL);
5661 g_free (master_ask);
5667 /* Change properties in 'connection' setting */
5668 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NULL);
5670 /* Add ethernet setting */
5671 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5672 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5674 } else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
5675 /* Build up the settings required for 'bridge' */
5676 gboolean success = FALSE;
5677 const char *ifname = NULL;
5678 const char *stp_c = NULL;
5680 const char *priority_c = NULL;
5681 char *priority = NULL;
5682 const char *fwd_delay_c = NULL;
5683 char *fwd_delay = NULL;
5684 const char *hello_time_c = NULL;
5685 char *hello_time = NULL;
5686 const char *max_age_c = NULL;
5687 char *max_age = NULL;
5688 const char *ageing_time_c = NULL;
5689 char *ageing_time = NULL;
5690 const char *mcast_snoop_c = NULL;
5691 char *mcast_snoop = NULL;
5692 gboolean stp_bool, mcast_snoop_bool;
5693 unsigned long stp_prio_int, fwd_delay_int, hello_time_int,
5694 max_age_int, ageing_time_int;
5695 const char *mac_c = NULL;
5697 nmc_arg_t exp_args[] = { {"stp", TRUE, &stp_c, FALSE},
5698 {"priority", TRUE, &priority_c, FALSE},
5699 {"forward-delay", TRUE, &fwd_delay_c, FALSE},
5700 {"hello-time", TRUE, &hello_time_c, FALSE},
5701 {"max-age", TRUE, &max_age_c, FALSE},
5702 {"ageing-time", TRUE, &ageing_time_c, FALSE},
5703 {"multicast-snooping", TRUE, &mcast_snoop_c, FALSE},
5704 {"mac", TRUE, &mac_c, FALSE},
5707 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5710 /* Also ask for all optional arguments if '--ask' is specified. */
5711 stp = g_strdup (stp_c);
5712 priority = g_strdup (priority_c);
5713 fwd_delay = g_strdup (fwd_delay_c);
5714 hello_time = g_strdup (hello_time_c);
5715 max_age = g_strdup (max_age_c);
5716 ageing_time = g_strdup (ageing_time_c);
5717 mcast_snoop = g_strdup (mcast_snoop_c);
5718 mac = g_strdup (mac_c);
5720 do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time,
5721 &max_age, &ageing_time, &mcast_snoop, &mac);
5723 /* Generate ifname if conneciton doesn't have one */
5724 ifname = nm_setting_connection_get_interface_name (s_con);
5726 char *bridge_ifname = unique_master_iface_ifname (all_connections, "nm-bridge");
5728 g_object_set (s_con,
5729 NM_SETTING_CONNECTION_INTERFACE_NAME, bridge_ifname,
5731 g_free (bridge_ifname);
5735 GError *tmp_err = NULL;
5736 if (!nmc_string_to_bool (stp, &stp_bool, &tmp_err)) {
5737 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5738 _("Error: 'stp': %s."), tmp_err->message);
5739 g_clear_error (&tmp_err);
5740 goto cleanup_bridge;
5744 GError *tmp_err = NULL;
5745 if (!nmc_string_to_bool (mcast_snoop, &mcast_snoop_bool, &tmp_err)) {
5746 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5747 _("Error: 'multicast-snooping': %s."), tmp_err->message);
5748 g_clear_error (&tmp_err);
5749 goto cleanup_bridge;
5753 /* Add 'bond' setting */
5754 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
5755 s_bridge = (NMSettingBridge *) nm_setting_bridge_new ();
5756 nm_connection_add_setting (connection, NM_SETTING (s_bridge));
5759 if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE,
5760 NM_SETTING_BRIDGE_PRIORITY, &stp_prio_int, error))
5761 goto cleanup_bridge;
5763 if (!bridge_prop_string_to_uint (fwd_delay, "forward-delay", NM_TYPE_SETTING_BRIDGE,
5764 NM_SETTING_BRIDGE_FORWARD_DELAY, &fwd_delay_int, error))
5765 goto cleanup_bridge;
5767 if (!bridge_prop_string_to_uint (hello_time, "hello-time", NM_TYPE_SETTING_BRIDGE,
5768 NM_SETTING_BRIDGE_HELLO_TIME, &hello_time_int, error))
5769 goto cleanup_bridge;
5771 if (!bridge_prop_string_to_uint (max_age, "max-age", NM_TYPE_SETTING_BRIDGE,
5772 NM_SETTING_BRIDGE_MAX_AGE, &max_age_int, error))
5773 goto cleanup_bridge;
5775 if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE,
5776 NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error))
5777 goto cleanup_bridge;
5778 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5779 goto cleanup_bridge;
5781 /* Set bridge options */
5783 g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, stp_bool, NULL);
5785 g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, stp_prio_int, NULL);
5787 g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, fwd_delay_int, NULL);
5789 g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, hello_time_int, NULL);
5791 g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL);
5793 g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL);
5795 g_object_set (s_bridge, NM_SETTING_BRIDGE_MULTICAST_SNOOPING, mcast_snoop_bool, NULL);
5797 g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, NULL);
5804 g_free (hello_time);
5806 g_free (ageing_time);
5807 g_free (mcast_snoop);
5812 } else if (!strcmp (con_type, "bridge-slave")) {
5813 /* Build up the settings required for 'bridge-slave' */
5814 gboolean success = FALSE;
5815 const char *master = NULL;
5816 char *master_ask = NULL;
5817 const char *type = NULL;
5818 const char *priority_c = NULL;
5819 char *priority = NULL;
5820 const char *path_cost_c = NULL;
5821 char *path_cost = NULL;
5822 const char *hairpin_c = NULL;
5823 char *hairpin = NULL;
5824 unsigned long prio_int, path_cost_int;
5825 gboolean hairpin_bool;
5826 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
5827 {"type", TRUE, &type, FALSE},
5828 {"priority", TRUE, &priority_c, FALSE},
5829 {"path-cost", TRUE, &path_cost_c, FALSE},
5830 {"hairpin", TRUE, &hairpin_c, FALSE},
5833 /* Set global variables for use in TAB completion */
5834 nmc_tab_completion.con_type = NM_SETTING_BRIDGE_SETTING_NAME;
5836 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5839 if (!complete_slave (s_con, all_connections, NM_SETTING_BRIDGE_SETTING_NAME, master, type, ask, error))
5842 /* Add 'bridge-port' setting */
5843 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
5844 s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new ();
5845 nm_connection_add_setting (connection, NM_SETTING (s_bridge_port));
5847 /* Also ask for all optional arguments if '--ask' is specified. */
5848 priority = g_strdup (priority_c);
5849 path_cost = g_strdup (path_cost_c);
5850 hairpin = g_strdup (hairpin_c);
5852 do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin);
5855 if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
5856 NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error))
5857 goto cleanup_bridge_slave;
5859 if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
5860 NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error))
5861 goto cleanup_bridge_slave;
5863 GError *tmp_err = NULL;
5864 if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) {
5865 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5866 _("Error: 'hairpin': %s."), tmp_err->message);
5867 g_clear_error (&tmp_err);
5868 goto cleanup_bridge_slave;
5873 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL);
5875 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL);
5877 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL);
5880 cleanup_bridge_slave:
5881 g_free (master_ask);
5888 /* Change properties in 'connection' setting */
5889 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NULL);
5891 /* Add ethernet setting */
5892 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5893 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5895 } else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) {
5896 /* Build up the settings required for 'vpn' */
5897 gboolean success = FALSE;
5898 const char *vpn_type = NULL;
5899 char *vpn_type_ask = NULL;
5900 const char *user_c = NULL;
5903 char *service_type = NULL;
5904 nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask},
5905 {"user", TRUE, &user_c, FALSE},
5908 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5911 if (!vpn_type && ask)
5912 vpn_type = vpn_type_ask = nmc_readline (PROMPT_VPN_TYPE);
5914 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5915 _("Error: 'vpn-type' is required."));
5919 vpn_type = g_strstrip (vpn_type_ask);
5921 if (!(st = nmc_string_is_valid (vpn_type, nmc_known_vpns, NULL))) {
5922 g_print (_("Warning: 'vpn-type': %s not known.\n"), vpn_type);
5925 service_type = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, st);
5927 /* Also ask for all optional arguments if '--ask' is specified. */
5928 user = g_strdup (user_c);
5930 do_questionnaire_vpn (&user);
5932 /* Add 'vpn' setting */
5933 s_vpn = (NMSettingVpn *) nm_setting_vpn_new ();
5934 nm_connection_add_setting (connection, NM_SETTING (s_vpn));
5936 g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL);
5937 g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL);
5941 g_free (vpn_type_ask);
5942 g_free (service_type);
5947 } else if (!strcmp (con_type, NM_SETTING_OLPC_MESH_SETTING_NAME)) {
5948 /* Build up the settings required for 'olpc' */
5949 gboolean success = FALSE;
5950 char *ssid_ask = NULL;
5951 const char *ssid = NULL;
5953 const char *channel_c = NULL;
5954 char *channel = NULL;
5956 const char *dhcp_anycast_c = NULL;
5957 char *dhcp_anycast = NULL;
5958 nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask},
5959 {"channel", TRUE, &channel_c, FALSE},
5960 {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE},
5963 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5967 ssid = ssid_ask = nmc_readline (_("SSID: "));
5969 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5970 _("Error: 'ssid' is required."));
5974 /* Also ask for all optional arguments if '--ask' is specified. */
5975 channel = g_strdup (channel_c);
5976 dhcp_anycast = g_strdup (dhcp_anycast_c);
5978 do_questionnaire_olpc (&channel, &dhcp_anycast);
5981 if (!nmc_string_to_uint (channel, TRUE, 1, 13, &chan)) {
5982 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5983 _("Error: 'channel': '%s' is not valid; use <1-13>."),
5988 if (!check_mac (dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", error))
5991 /* Add OLPC mesh setting */
5992 s_olpc_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new ();
5993 nm_connection_add_setting (connection, NM_SETTING (s_olpc_mesh));
5995 ssid_bytes = g_bytes_new (ssid, strlen (ssid));
5996 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_SSID, ssid_bytes, NULL);
5998 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, chan, NULL);
6000 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, 1, NULL);
6002 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, dhcp_anycast, NULL);
6003 g_bytes_unref (ssid_bytes);
6009 g_free (dhcp_anycast);
6013 } else if (!strcmp (con_type, NM_SETTING_ADSL_SETTING_NAME)) {
6014 /* Build up the settings required for 'adsl' */
6015 gboolean success = FALSE;
6016 char *username_ask = NULL;
6017 const char *username = NULL;
6018 char *protocol_ask = NULL, *protocol = NULL;
6019 const char *protocol_c = NULL;
6020 const char *password_c = NULL;
6021 char *password = NULL;
6022 const char *encapsulation_c = NULL;
6023 char *encapsulation = NULL;
6024 nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask},
6025 {"protocol", TRUE, &protocol_c, !ask},
6026 {"password", TRUE, &password_c, FALSE},
6027 {"encapsulation", TRUE, &encapsulation_c, FALSE},
6030 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6033 if (!username && ask)
6034 username = username_ask = nmc_readline (_("Username: "));
6036 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6037 _("Error: 'username' is required."));
6041 #define PROMPT_ADSL_PROTO "(" NM_SETTING_ADSL_PROTOCOL_PPPOA "/" NM_SETTING_ADSL_PROTOCOL_PPPOE "/" NM_SETTING_ADSL_PROTOCOL_IPOATM "): "
6042 if (!protocol_c && ask)
6043 protocol_c = protocol_ask = nmc_readline (_("Protocol %s"), PROMPT_ADSL_PROTO);
6045 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6046 _("Error: 'protocol' is required."));
6049 protocol = g_strdup (protocol_c);
6050 if (!check_adsl_protocol (&protocol, error))
6053 /* Also ask for all optional arguments if '--ask' is specified. */
6054 password = g_strdup (password_c);
6055 encapsulation = g_strdup (encapsulation_c);
6057 do_questionnaire_adsl (show_secrets, &password, &encapsulation);
6059 if (!check_adsl_encapsulation (&encapsulation, error))
6062 /* Add ADSL setting */
6063 s_adsl = (NMSettingAdsl *) nm_setting_adsl_new ();
6064 nm_connection_add_setting (connection, NM_SETTING (s_adsl));
6066 g_object_set (s_adsl,
6067 NM_SETTING_ADSL_USERNAME, username,
6068 NM_SETTING_ADSL_PROTOCOL, protocol,
6069 NM_SETTING_ADSL_PASSWORD, password,
6070 NM_SETTING_ADSL_ENCAPSULATION, encapsulation,
6075 g_free (username_ask);
6078 g_free (protocol_ask);
6079 g_free (encapsulation);
6084 } else if (!strcmp (con_type, NM_SETTING_MACVLAN_SETTING_NAME)) {
6085 /* Build up the settings required for 'macvlan' */
6086 gboolean success = FALSE;
6087 const char *parent = NULL;
6088 char *parent_ask = NULL;
6089 const char *mode = NULL;
6090 char *mode_ask = NULL;
6091 const char *tap_c = NULL;
6093 NMSettingMacvlanMode mode_enum;
6094 gboolean valid_mac = FALSE;
6095 gboolean tap_bool = FALSE;
6096 nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask},
6097 {"mode", TRUE, &mode, !ask},
6098 {"tap", TRUE, &tap_c, FALSE},
6101 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6105 parent = parent_ask = nmc_readline (_("MACVLAN parent device or connection UUID: "));
6107 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6108 _("Error: 'dev' is required."));
6112 if ( !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN))
6113 && !nm_utils_is_uuid (parent)
6114 && !nm_utils_iface_valid_name (parent)) {
6115 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6116 _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
6118 goto cleanup_macvlan;
6122 mode = mode_ask = nmc_readline (PROMPT_MACVLAN_MODE);
6124 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6125 _("Error: 'mode' is required."));
6129 if (!nm_utils_enum_from_str (nm_setting_macvlan_mode_get_type(), mode, (int *) &mode_enum, NULL)) {
6130 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6131 _("Error: 'mode' is not valid."));
6135 /* Also ask for all optional arguments if '--ask' is specified. */
6136 tap = g_strdup (tap_c);
6138 do_questionnaire_macvlan (&tap);
6141 GError *tmp_err = NULL;
6142 if (!nmc_string_to_bool (tap, &tap_bool, &tmp_err)) {
6143 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6144 _("Error: 'tap': %s."), tmp_err->message);
6145 g_clear_error (&tmp_err);
6146 goto cleanup_macvlan;
6150 /* Add 'macvlan' setting */
6151 s_macvlan = (NMSettingMacvlan *) nm_setting_macvlan_new ();
6152 nm_connection_add_setting (connection, NM_SETTING (s_macvlan));
6154 /* Add 'wired' setting if necessary */
6156 s_wired = (NMSettingWired *) nm_setting_wired_new ();
6157 nm_connection_add_setting (connection, NM_SETTING (s_wired));
6158 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL);
6161 /* Set 'macvlan' properties */
6163 g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, parent, NULL);
6164 g_object_set (s_macvlan, NM_SETTING_MACVLAN_MODE, mode_enum, NULL);
6165 g_object_set (s_macvlan, NM_SETTING_MACVLAN_TAP, tap_bool, NULL);
6169 g_free (parent_ask);
6176 } else if (!strcmp (con_type, NM_SETTING_TUN_SETTING_NAME)) {
6177 /* Build up the settings required for 'tun' */
6178 gboolean success = FALSE;
6179 const char *mode_c = NULL;
6180 char *mode_ask = NULL, *mode = NULL;
6181 NMSettingTunMode mode_enum;
6182 const char *owner_c = NULL, *group_c = NULL;
6183 char *owner = NULL, *group = NULL;
6184 const char *pi_c = NULL, *vnet_hdr_c = NULL, *multi_queue_c = NULL;
6185 char *pi = NULL, *vnet_hdr = NULL, *multi_queue = NULL;
6186 gboolean pi_bool, vnet_hdr_bool, multi_queue_bool;
6187 nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !ask},
6188 {"owner", TRUE, &owner_c, FALSE},
6189 {"group", TRUE, &group_c, FALSE},
6190 {"pi", TRUE, &pi_c, FALSE},
6191 {"vnet-hdr", TRUE, &vnet_hdr_c, FALSE},
6192 {"multi-queue", TRUE, &multi_queue_c, FALSE},
6195 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6198 if (!mode_c && ask) {
6199 mode_ask = nmc_readline (_("Mode %s"), PROMPT_TUN_MODE);
6200 mode_ask = mode_ask ? mode_ask : g_strdup ("tun");
6204 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6205 _("Error: 'mode' is required."));
6208 mode = g_strdup (mode_c);
6209 if (!check_tun_mode (&mode, error))
6212 if (owner && !check_user_group_id (owner, error))
6214 if (group && !check_user_group_id (group, error))
6217 owner = g_strdup (owner_c);
6218 group = g_strdup (group_c);
6219 pi = g_strdup (pi_c);
6220 vnet_hdr = g_strdup (vnet_hdr_c);
6221 multi_queue = g_strdup (multi_queue_c);
6223 do_questionnaire_tun (&owner, &group, &pi, &vnet_hdr, &multi_queue);
6226 GError *tmp_err = NULL;
6228 if (!nmc_string_to_bool (pi, &pi_bool, &tmp_err)) {
6229 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6230 _("Error: 'pi': %s."), tmp_err->message);
6231 g_clear_error (&tmp_err);
6237 GError *tmp_err = NULL;
6239 if (!nmc_string_to_bool (vnet_hdr, &vnet_hdr_bool, &tmp_err)) {
6240 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6241 _("Error: 'vnet-hdr': %s."), tmp_err->message);
6242 g_clear_error (&tmp_err);
6248 GError *tmp_err = NULL;
6250 if (!nmc_string_to_bool (multi_queue, &multi_queue_bool, &tmp_err)) {
6251 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6252 _("Error: 'multi-queue': %s."), tmp_err->message);
6253 g_clear_error (&tmp_err);
6257 /* Add 'tun' setting */
6258 s_tun = (NMSettingTun *) nm_setting_tun_new ();
6259 nm_connection_add_setting (connection, NM_SETTING (s_tun));
6260 mode_enum = !strcmp (mode, "tun") ? NM_SETTING_TUN_MODE_TUN : NM_SETTING_TUN_MODE_TAP;
6262 g_object_set (s_tun,
6263 NM_SETTING_TUN_MODE, mode_enum,
6264 NM_SETTING_TUN_OWNER, owner,
6265 NM_SETTING_TUN_GROUP, group,
6268 g_object_set (s_tun, NM_SETTING_TUN_PI, pi_bool, NULL);
6270 g_object_set (s_tun, NM_SETTING_TUN_VNET_HDR, vnet_hdr_bool, NULL);
6272 g_object_set (s_tun, NM_SETTING_TUN_MULTI_QUEUE, multi_queue_bool, NULL);
6282 g_free (multi_queue);
6286 } else if (!strcmp (con_type, NM_SETTING_IP_TUNNEL_SETTING_NAME)) {
6287 /* Build up the settings required for 'ip-tunnel' */
6288 const char *mode_c = NULL, *local_c = NULL, *remote_c = NULL;
6289 char *mode_ask = NULL, *remote_ask = NULL, *local = NULL;
6290 const char *parent_c = NULL;
6291 char *parent = NULL;
6292 gboolean success = FALSE;
6293 NMIPTunnelMode mode_enum;
6294 nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !ask},
6295 {"local", TRUE, &local_c, FALSE},
6296 {"remote", TRUE, &remote_c, !ask},
6297 {"dev", TRUE, &parent_c, FALSE},
6300 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6304 mode_c = mode_ask = nmc_readline (PROMPT_IP_TUNNEL_MODE);
6306 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6307 _("Error: 'mode' is required."));
6308 goto cleanup_tunnel;
6311 if (!nm_utils_enum_from_str (nm_ip_tunnel_mode_get_type (),
6312 mode_c, (int *) &mode_enum, NULL)) {
6313 gs_free const char **values = NULL;
6314 gs_free char *values_str = NULL;
6316 values = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
6317 NM_IP_TUNNEL_MODE_UNKNOWN + 1,
6319 values_str = g_strjoinv (",", (char **) values);
6320 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6321 _("Error: 'mode': '%s' is not valid, use one of %s"),
6322 mode_c, values_str);
6323 goto cleanup_tunnel;
6326 if (!remote_c && ask)
6327 remote_c = remote_ask = nmc_readline (_("Remote endpoint: "));
6329 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6330 _("Error: 'remote' is required."));
6331 goto cleanup_tunnel;
6334 if ( !nm_utils_ipaddr_valid (AF_INET, remote_c)
6335 && !nm_utils_ipaddr_valid (AF_INET6, remote_c)) {
6336 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6337 _("Error: 'remote': '%s' is not valid; must be an IP address"),
6339 goto cleanup_tunnel;
6342 local = g_strdup (local_c);
6343 parent = g_strdup (parent_c);
6345 do_questionnaire_ip_tunnel (&local, &parent);
6348 && !nm_utils_ipaddr_valid (AF_INET, local)
6349 && !nm_utils_ipaddr_valid (AF_INET6, local)) {
6350 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6351 _("Error: 'local': '%s' is not valid; must be an IP address"),
6353 goto cleanup_tunnel;
6357 if ( !nm_utils_is_uuid (parent)
6358 && !nm_utils_iface_valid_name (parent)) {
6359 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6360 _("Error: 'dev': '%s' is neither UUID nor interface name."),
6362 goto cleanup_tunnel;
6366 /* Add 'tunnel' setting */
6367 s_ip_tunnel = (NMSettingIPTunnel *) nm_setting_ip_tunnel_new ();
6368 nm_connection_add_setting (connection, NM_SETTING (s_ip_tunnel));
6370 /* Set 'tunnel' properties */
6371 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_MODE, mode_enum, NULL);
6372 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_REMOTE, remote_c, NULL);
6374 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_LOCAL, local, NULL);
6376 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_PARENT, parent, NULL);
6378 /* Set default values for IPv6 tunnels */
6379 if (nm_utils_ipaddr_valid (AF_INET6, remote_c)) {
6380 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_TOS, 64, NULL);
6381 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT, 4, NULL);
6386 g_free (remote_ask);
6393 } else if (!strcmp (con_type, NM_SETTING_VXLAN_SETTING_NAME)) {
6394 /* Build up the settings required for 'vxlan' */
6395 gboolean success = FALSE;
6396 char *id_ask = NULL;
6397 const char *id = NULL;
6398 char *remote_ask = NULL;
6399 const char *remote = NULL;
6400 const char *parent_c = NULL, *local_c = NULL;
6401 const char *src_port_min_c = NULL, *src_port_max_c = NULL;
6402 const char *dst_port_c = NULL;
6403 char *parent = NULL, *local = NULL;
6404 char *src_port_min = NULL, *src_port_max = NULL, *dst_port = NULL;
6405 unsigned long int vni;
6406 unsigned long sport_min = G_MAXULONG, sport_max = G_MAXULONG;
6407 unsigned long dport = G_MAXULONG;
6408 nmc_arg_t exp_args[] = { {"id", TRUE, &id, !ask},
6409 {"remote", TRUE, &remote, !ask},
6410 {"dev", TRUE, &parent_c, FALSE},
6411 {"local", TRUE, &local_c, FALSE},
6412 {"source-port-min", TRUE, &src_port_min_c, FALSE},
6413 {"source-port-max", TRUE, &src_port_max_c, FALSE},
6414 {"destination-port", TRUE, &dst_port_c, FALSE},
6417 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6421 id = id_ask = nmc_readline (_("VXLAN ID: "));
6423 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6424 _("Error: 'id' is required."));
6429 remote = remote_ask = nmc_readline (_("Remote: "));
6431 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6432 _("Error: 'remote' is required."));
6436 if (!nmc_string_to_uint (id, TRUE, 0, (1UL << 24) - 1, &vni)) {
6437 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6438 _("Error: 'id': '%s' is not valid; use <0-16777215>."), id);
6442 parent = g_strdup (parent_c);
6443 local = g_strdup (local_c);
6444 src_port_min = g_strdup (src_port_min_c);
6445 src_port_max = g_strdup (src_port_max_c);
6446 dst_port = g_strdup (dst_port_c);
6449 do_questionnaire_vxlan (&parent, &local, &src_port_min, &src_port_max, &dst_port);
6452 if ( !nm_utils_is_uuid (parent)
6453 && !nm_utils_iface_valid_name (parent)) {
6454 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6455 _("Error: 'dev': '%s' is neither UUID nor interface name."),
6461 if ( !nm_utils_ipaddr_valid (AF_INET, remote)
6462 && !nm_utils_ipaddr_valid (AF_INET6, remote)) {
6463 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6464 _("Error: 'remote': '%s' is not a valid IP address"),
6470 if ( !nm_utils_ipaddr_valid (AF_INET, local)
6471 && !nm_utils_ipaddr_valid (AF_INET6, local)) {
6472 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6473 _("Error: 'local': '%s' is not a valid IP address"),
6480 if (!nmc_string_to_uint (src_port_min, TRUE, 0, 65535, &sport_min)) {
6481 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6482 _("Error: 'source-port-min': %s is not valid; use <0-65535>."),
6489 if (!nmc_string_to_uint (src_port_max, TRUE, 0, 65535, &sport_max)) {
6490 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6491 _("Error: 'source-port-max': %s is not valid; use <0-65535>."),
6498 if (!nmc_string_to_uint (dst_port, TRUE, 0, 65535, &dport)) {
6499 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6500 _("Error: 'destination-port': %s is not valid; use <0-65535>."),
6506 /* Add 'vxlan' setting */
6507 s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new ();
6508 nm_connection_add_setting (connection, NM_SETTING (s_vxlan));
6510 g_object_set (s_vxlan, NM_SETTING_VXLAN_ID, (guint) vni, NULL);
6511 g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE, remote, NULL);
6512 g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL, local, NULL);
6513 g_object_set (s_vxlan, NM_SETTING_VXLAN_PARENT, parent, NULL);
6515 if (sport_min != G_MAXULONG)
6516 g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MIN, sport_min, NULL);
6517 if (sport_max != G_MAXULONG)
6518 g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MAX, sport_max, NULL);
6519 if (dport != G_MAXULONG)
6520 g_object_set (s_vxlan, NM_SETTING_VXLAN_DESTINATION_PORT, dport, NULL);
6526 g_free (remote_ask);
6529 g_free (src_port_min);
6530 g_free (src_port_max);
6534 } else if (!strcmp (con_type, NM_SETTING_GENERIC_SETTING_NAME)) {
6535 /* Add 'generic' setting */
6536 s_generic = (NMSettingGeneric *) nm_setting_generic_new ();
6537 nm_connection_add_setting (connection, NM_SETTING (s_generic));
6539 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6540 _("Error: '%s' is not a valid connection type."),
6545 if (!nm_setting_connection_get_slave_type (s_con)) {
6546 /* Read and add IP configuration */
6547 NMIPAddress *ip4addr = NULL, *ip6addr = NULL;
6548 const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL;
6549 nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE},
6550 {"ip6", TRUE, &ip6, FALSE}, {"gw6", TRUE, &gw6, FALSE},
6556 /* reset 'found' flag */
6557 for (p = exp_args; p->name; p++)
6560 ip4 = gw4 = ip6 = gw6 = NULL;
6562 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, NULL))
6565 /* coverity[dead_error_begin] */
6567 ip4addr = nmc_parse_and_build_address (AF_INET, ip4, error);
6569 g_prefix_error (error, _("Error: "));
6572 add_ip4_address_to_connection (ip4addr, connection);
6575 /* coverity[dead_error_begin] */
6577 NMSettingIPConfig *s_ip = nm_connection_get_setting_ip4_config (connection);
6580 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6581 _("Error: IPv4 gateway specified without IPv4 addresses"));
6583 } else if (nm_setting_ip_config_get_gateway (s_ip)) {
6584 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6585 _("Error: multiple IPv4 gateways specified"));
6587 } else if (!nm_utils_ipaddr_valid (AF_INET, gw4)) {
6588 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6589 _("Error: Invalid IPv4 gateway '%s'"),
6594 NM_SETTING_IP_CONFIG_GATEWAY, gw4,
6598 /* coverity[dead_error_begin] */
6600 ip6addr = nmc_parse_and_build_address (AF_INET6, ip6, error);
6602 g_prefix_error (error, _("Error: "));
6605 add_ip6_address_to_connection (ip6addr, connection);
6608 /* coverity[dead_error_begin] */
6610 NMSettingIPConfig *s_ip = nm_connection_get_setting_ip6_config (connection);
6613 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6614 _("Error: IPv6 gateway specified without IPv6 addresses"));
6616 } else if (nm_setting_ip_config_get_gateway (s_ip)) {
6617 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6618 _("Error: multiple IPv6 gateways specified"));
6620 } else if (!nm_utils_ipaddr_valid (AF_INET, gw6)) {
6621 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6622 _("Error: Invalid IPv6 gateway '%s'"),
6627 NM_SETTING_IP_CONFIG_GATEWAY, gw6,
6632 /* Ask for addresses if '--ask' is specified. */
6634 do_questionnaire_ip (connection);
6638 /* Set extra connection properties. */
6639 nmc_arg_t exp_args[] = { {"--", FALSE, NULL, TRUE},
6642 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6645 if (!read_connection_properties (connection, argc, argv, error))
6655 } AddConnectionInfo;
6658 add_connection_cb (GObject *client,
6659 GAsyncResult *result,
6662 AddConnectionInfo *info = (AddConnectionInfo *) user_data;
6663 NmCli *nmc = info->nmc;
6664 NMRemoteConnection *connection;
6665 GError *error = NULL;
6667 connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
6669 g_string_printf (nmc->return_text,
6670 _("Error: Failed to add '%s' connection: %s"),
6671 info->con_name, error->message);
6672 g_error_free (error);
6673 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
6675 g_print (_("Connection '%s' (%s) successfully added.\n"),
6676 nm_connection_get_id (NM_CONNECTION (connection)),
6677 nm_connection_get_uuid (NM_CONNECTION (connection)));
6678 g_object_unref (connection);
6681 g_free (info->con_name);
6687 add_new_connection (gboolean persistent,
6689 NMConnection *connection,
6690 GAsyncReadyCallback callback,
6693 nm_client_add_connection_async (client, connection, persistent,
6694 NULL, callback, user_data);
6698 update_connection (gboolean persistent,
6699 NMRemoteConnection *connection,
6700 GAsyncReadyCallback callback,
6703 nm_remote_connection_commit_changes_async (connection, persistent,
6704 NULL, callback, user_data);
6708 gen_func_vpn_types (const char *text, int state)
6710 return nmc_rl_gen_func_basic (text, state, nmc_known_vpns);
6714 gen_func_bool_values_l10n (const char *text, int state)
6716 const char *words[] = { WORD_LOC_YES, WORD_LOC_NO, NULL };
6717 return nmc_rl_gen_func_basic (text, state, words);
6721 gen_func_wifi_mode (const char *text, int state)
6723 const char *words[] = { "infrastructure", "ap", "adhoc", NULL };
6724 return nmc_rl_gen_func_basic (text, state, words);
6728 gen_func_ib_type (const char *text, int state)
6730 const char *words[] = { "datagram", "connected", NULL };
6731 return nmc_rl_gen_func_basic (text, state, words);
6735 gen_func_bt_type (const char *text, int state)
6737 const char *words[] = { "panu", "dun-gsm", "dun-cdma", NULL };
6738 return nmc_rl_gen_func_basic (text, state, words);
6742 gen_func_bond_mode (const char *text, int state)
6744 const char *words[] = { "balance-rr", "active-backup", "balance-xor", "broadcast",
6745 "802.3ad", "balance-tlb", "balance-alb", NULL };
6746 return nmc_rl_gen_func_basic (text, state, words);
6749 gen_func_bond_mon_mode (const char *text, int state)
6751 const char *words[] = { "miimon", "arp", NULL };
6752 return nmc_rl_gen_func_basic (text, state, words);
6756 gen_func_adsl_proto (const char *text, int state)
6758 const char *words[] = { "pppoe", "pppoa", "ipoatm", NULL };
6759 return nmc_rl_gen_func_basic (text, state, words);
6763 gen_func_adsl_encap (const char *text, int state)
6765 const char *words[] = { "vcmux", "llc", NULL };
6766 return nmc_rl_gen_func_basic (text, state, words);
6770 gen_func_tun_mode (const char *text, int state)
6772 const char *words[] = { "tun", "tap", NULL };
6773 return nmc_rl_gen_func_basic (text, state, words);
6777 gen_func_ip_tunnel_mode (const char *text, int state)
6779 gs_free const char **words = NULL;
6781 words = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
6782 NM_IP_TUNNEL_MODE_UNKNOWN + 1,
6784 return nmc_rl_gen_func_basic (text, state, words);
6788 gen_func_macvlan_mode (const char *text, int state)
6790 gs_free const char **words = NULL;
6792 words = nm_utils_enum_get_values (nm_setting_macvlan_mode_get_type(),
6793 NM_SETTING_MACVLAN_MODE_UNKNOWN + 1,
6795 return nmc_rl_gen_func_basic (text, state, words);
6799 gen_func_master_ifnames (const char *text, int state)
6805 NMSettingConnection *s_con;
6806 const char *con_type, *ifname;
6808 if (!nm_cli.connections)
6811 /* Disable appending space after completion */
6812 rl_completion_append_character = '\0';
6814 ifnames = g_ptr_array_sized_new (20);
6815 for (i = 0; i < nm_cli.connections->len; i++) {
6816 con = NM_CONNECTION (nm_cli.connections->pdata[i]);
6817 s_con = nm_connection_get_setting_connection (con);
6819 con_type = nm_setting_connection_get_connection_type (s_con);
6820 if (g_strcmp0 (con_type, nmc_tab_completion.con_type) != 0)
6822 ifname = nm_connection_get_interface_name (con);
6823 g_ptr_array_add (ifnames, (gpointer) ifname);
6825 g_ptr_array_add (ifnames, (gpointer) NULL);
6827 ret = nmc_rl_gen_func_basic (text, state, (const char **) ifnames->pdata);
6829 g_ptr_array_free (ifnames, TRUE);
6834 is_single_word (const char* line)
6838 n1 = strspn (line, " \t");
6839 n2 = strcspn (line+n1, " \t\0") + n1;
6840 n3 = strspn (line+n2, " \t");
6849 nmcli_con_add_tab_completion (const char *text, int start, int end)
6851 char **match_array = NULL;
6852 rl_compentry_func_t *generator_func = NULL;
6854 /* Disable readline's default filename completion */
6855 rl_attempted_completion_over = 1;
6857 /* Restore standard append character to space */
6858 rl_completion_append_character = ' ';
6860 if (!is_single_word (rl_line_buffer))
6863 if (g_strcmp0 (rl_prompt, PROMPT_CON_TYPE) == 0)
6864 generator_func = gen_connection_types;
6865 else if (g_strcmp0 (rl_prompt, PROMPT_VPN_TYPE) == 0)
6866 generator_func = gen_func_vpn_types;
6867 else if (g_strcmp0 (rl_prompt, PROMPT_MASTER) == 0)
6868 generator_func = gen_func_master_ifnames;
6869 else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL))
6870 || g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, ":"))
6871 || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL))
6872 || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, ":")))
6873 generator_func = gen_func_bool_values_l10n;
6874 else if (g_str_has_suffix (rl_prompt, PROMPT_WIFI_MODE))
6875 generator_func = gen_func_wifi_mode;
6876 else if (g_str_has_suffix (rl_prompt, PROMPT_IB_MODE))
6877 generator_func = gen_func_ib_type;
6878 else if (g_str_has_suffix (rl_prompt, PROMPT_BT_TYPE))
6879 generator_func = gen_func_bt_type;
6880 else if (g_str_has_prefix (rl_prompt, PROMPT_BOND_MODE))
6881 generator_func = gen_func_bond_mode;
6882 else if (g_str_has_suffix (rl_prompt, PROMPT_BOND_MON_MODE))
6883 generator_func = gen_func_bond_mon_mode;
6884 else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_PROTO))
6885 generator_func = gen_func_adsl_proto;
6886 else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_ENCAP))
6887 generator_func = gen_func_adsl_encap;
6888 else if (g_str_has_suffix (rl_prompt, PROMPT_TUN_MODE))
6889 generator_func = gen_func_tun_mode;
6890 else if (g_str_has_suffix (rl_prompt, PROMPT_IP_TUNNEL_MODE))
6891 generator_func = gen_func_ip_tunnel_mode;
6892 else if (g_str_has_suffix (rl_prompt, PROMPT_MACVLAN_MODE))
6893 generator_func = gen_func_macvlan_mode;
6896 match_array = rl_completion_matches (text, generator_func);
6901 static NMCResultCode
6902 do_connection_add (NmCli *nmc, int argc, char **argv)
6904 NMConnection *connection = NULL;
6905 NMSettingConnection *s_con;
6907 char *default_name = NULL;
6908 const char *type = NULL;
6909 char *type_ask = NULL;
6910 const char *con_name = NULL;
6911 const char *autoconnect = NULL;
6912 gboolean auto_bool = TRUE;
6913 const char *ifname = NULL;
6914 char *ifname_ask = NULL;
6915 gboolean ifname_mandatory = TRUE;
6916 const char *save = NULL;
6917 gboolean save_bool = TRUE;
6918 const char *master = NULL;
6919 const char *checked_master = NULL;
6920 const char *slave_type = NULL;
6921 AddConnectionInfo *info = NULL;
6922 const char *setting_name;
6923 GError *error = NULL;
6924 nmc_arg_t exp_args[] = { {"type", TRUE, &type, !nmc->ask},
6925 {"con-name", TRUE, &con_name, FALSE},
6926 {"autoconnect", TRUE, &autoconnect, FALSE},
6927 {"ifname", TRUE, &ifname, FALSE},
6928 {"save", TRUE, &save, FALSE},
6929 {"master", TRUE, &master, FALSE},
6930 {"slave-type", TRUE, &slave_type, FALSE},
6933 rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion;
6935 nmc->return_value = NMC_RESULT_SUCCESS;
6937 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) {
6938 g_string_assign (nmc->return_text, error->message);
6939 nmc->return_value = error->code;
6940 g_clear_error (&error);
6944 if (!type && nmc->ask) {
6945 char *types_tmp = get_valid_options_string (nmc_valid_connection_types, NULL);
6946 g_print ("Valid types: [%s]\n", types_tmp);
6947 type = type_ask = nmc_readline (PROMPT_CON_TYPE);
6951 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
6952 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6956 type = g_strstrip (type_ask);
6958 if (!(setting_name = check_valid_name (type, nmc_valid_connection_types, NULL, &error))) {
6959 g_string_printf (nmc->return_text, _("Error: invalid connection type; %s."),
6961 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6962 g_clear_error (&error);
6966 GError *tmp_err = NULL;
6967 if (!nmc_string_to_bool (autoconnect, &auto_bool, &tmp_err)) {
6968 g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."),
6970 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6971 g_clear_error (&tmp_err);
6976 GError *tmp_err = NULL;
6977 if (!nmc_string_to_bool (save, &save_bool, &tmp_err)) {
6978 g_string_printf (nmc->return_text, _("Error: 'save': %s."),
6980 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6981 g_clear_error (&tmp_err);
6986 /* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */
6987 if ( strcmp (type, NM_SETTING_BOND_SETTING_NAME) == 0
6988 || strcmp (type, NM_SETTING_TEAM_SETTING_NAME) == 0
6989 || strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME) == 0
6990 || strcmp (type, NM_SETTING_VLAN_SETTING_NAME) == 0)
6991 ifname_mandatory = FALSE;
6993 if (!ifname && ifname_mandatory) {
6995 ifname = ifname_ask = nmc_readline (_("Interface name [*]: "));
6997 ifname = ifname_ask = g_strdup ("*");
7000 g_string_printf (nmc->return_text, _("Error: 'ifname' argument is required."));
7002 g_string_printf (nmc->return_text, _("Error: mandatory 'ifname' not seen before '%s'."),
7004 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7009 if (!nm_utils_iface_valid_name (ifname) && strcmp (ifname, "*") != 0) {
7010 g_string_printf (nmc->return_text,
7011 _("Error: 'ifname': '%s' is not a valid interface nor '*'."),
7013 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7016 /* Special value of '*' means no specific interface name */
7017 if (strcmp (ifname, "*") == 0)
7021 /* Create a new connection object */
7022 connection = nm_simple_connection_new ();
7024 /* Build up the 'connection' setting */
7025 s_con = (NMSettingConnection *) nm_setting_connection_new ();
7026 uuid = nm_utils_uuid_generate ();
7028 default_name = g_strdup (con_name);
7030 char *try_name = ifname ?
7031 g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname)
7032 : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types));
7033 default_name = nmc_unique_connection_name (nmc->connections, try_name);
7038 /* Verify master argument */
7039 checked_master = normalized_master_for_slave (nmc->connections, master, slave_type, &slave_type);
7041 g_object_set (s_con,
7042 NM_SETTING_CONNECTION_ID, default_name,
7043 NM_SETTING_CONNECTION_UUID, uuid,
7044 NM_SETTING_CONNECTION_TYPE, setting_name,
7045 NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool,
7046 NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
7047 NM_SETTING_CONNECTION_MASTER, checked_master,
7048 NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
7051 g_free (default_name);
7052 nm_connection_add_setting (connection, NM_SETTING (s_con));
7054 if (!complete_connection_by_type (connection,
7062 g_string_assign (nmc->return_text, error->message);
7063 nmc->return_value = error->code;
7064 g_clear_error (&error);
7070 info = g_malloc0 (sizeof (AddConnectionInfo));
7072 info->con_name = g_strdup (nm_connection_get_id (connection));
7074 /* Tell the settings service to add the new connection */
7075 add_new_connection (save_bool,
7082 g_object_unref (connection);
7084 return nmc->return_value;
7088 g_object_unref (connection);
7090 g_free (ifname_ask);
7092 return nmc->return_value;
7096 /*----------------------------------------------------------------------------*/
7097 /* Functions for readline TAB completion in editor */
7100 uuid_display_hook (char **array, int len, int max_len)
7106 for (i = 1; i <= len; i++) {
7107 con = nmc_find_connection (nmc_tab_completion.nmc->connections, "uuid", array[i], NULL);
7108 id = con ? nm_connection_get_id (con) : NULL;
7110 tmp = g_strdup_printf ("%s (%s)", array[i], id);
7113 if (max < strlen (id))
7117 rl_display_match_list (array, len, max_len + max + 3);
7118 rl_forced_update_display ();
7122 gen_nmcli_cmds_menu (const char *text, int state)
7124 const char *words[] = { "goto", "set", "remove", "describe", "print", "verify",
7125 "save", "activate", "back", "help", "quit", "nmcli",
7127 return nmc_rl_gen_func_basic (text, state, words);
7131 gen_nmcli_cmds_submenu (const char *text, int state)
7133 const char *words[] = { "set", "add", "change", "remove", "describe",
7134 "print", "back", "help", "quit",
7136 return nmc_rl_gen_func_basic (text, state, words);
7140 gen_cmd_nmcli (const char *text, int state)
7142 const char *words[] = { "status-line", "save-confirmation", "show-secrets", "prompt-color", NULL };
7143 return nmc_rl_gen_func_basic (text, state, words);
7147 gen_cmd_nmcli_prompt_color (const char *text, int state)
7149 const char *words[] = { "normal", "black", "red", "green", "yellow",
7150 "blue", "magenta", "cyan", "white", NULL };
7151 return nmc_rl_gen_func_basic (text, state, words);
7155 gen_func_bool_values (const char *text, int state)
7157 const char *words[] = { "yes", "no", NULL };
7158 return nmc_rl_gen_func_basic (text, state, words);
7162 gen_cmd_verify0 (const char *text, int state)
7164 const char *words[] = { "all", "fix", NULL };
7165 return nmc_rl_gen_func_basic (text, state, words);
7169 gen_cmd_print0 (const char *text, int state)
7171 static char **words = NULL;
7177 const char *setting_name;
7180 settings = nm_connection_to_dbus (nmc_tab_completion.connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
7181 words = g_new (char *, g_variant_n_children (settings) + 2);
7182 g_variant_iter_init (&iter, settings);
7183 while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, NULL))
7184 words [i++] = g_strdup (setting_name);
7185 words[i++] = g_strdup ("all");
7187 g_variant_unref (settings);
7191 ret = nmc_rl_gen_func_basic (text, state, (const char **) words);
7201 gen_cmd_print2 (const char *text, int state)
7203 const char *words[] = { "setting", "connection", "all", NULL };
7204 return nmc_rl_gen_func_basic (text, state, words);
7208 gen_cmd_save (const char *text, int state)
7210 const char *words[] = { "persistent", "temporary", NULL };
7211 return nmc_rl_gen_func_basic (text, state, words);
7215 gen_connection_types (const char *text, int state)
7217 static int list_idx, len;
7218 const char *c_type, *a_type;
7222 len = strlen (text);
7225 while (nmc_valid_connection_types[list_idx].name) {
7226 a_type = nmc_valid_connection_types[list_idx].alias;
7227 c_type = nmc_valid_connection_types[list_idx].name;
7229 if (a_type && !strncmp (text, a_type, len))
7230 return g_strdup (a_type);
7231 if (c_type && !strncmp (text, c_type, len))
7232 return g_strdup (c_type);
7239 gen_setting_names (const char *text, int state)
7241 static int list_idx, len, is_slv;
7242 const char *s_name, *a_name;
7243 const NameItem *valid_settings_arr;
7244 NMSettingConnection *s_con;
7245 const char *s_type = NULL;
7250 len = strlen (text);
7255 valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type);
7256 if (!valid_settings_arr)
7258 while (valid_settings_arr[list_idx].name) {
7259 a_name = valid_settings_arr[list_idx].alias;
7260 s_name = valid_settings_arr[list_idx].name;
7262 if (len == 0 && a_name)
7263 return g_strdup_printf ("%s (%s)", s_name, a_name);
7264 if (a_name && !strncmp (text, a_name, len))
7265 return g_strdup (a_name);
7266 if (s_name && !strncmp (text, s_name, len))
7267 return g_strdup (s_name);
7270 /* Let's give a try to parameters related to slave type */
7276 s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
7278 s_type = nm_setting_connection_get_slave_type (s_con);
7279 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
7280 valid_settings_arr = get_valid_settings_array (slv_type);
7283 while (valid_settings_arr[list_idx].name) {
7284 a_name = valid_settings_arr[list_idx].alias;
7285 s_name = valid_settings_arr[list_idx].name;
7287 if (len == 0 && a_name)
7288 return g_strdup_printf ("%s (%s)", s_name, a_name);
7289 if (a_name && !strncmp (text, a_name, len))
7290 return g_strdup (a_name);
7291 if (s_name && !strncmp (text, s_name, len))
7292 return g_strdup (s_name);
7299 gen_property_names (const char *text, int state)
7301 NMSetting *setting = NULL;
7302 char **valid_props = NULL;
7304 const char *line = rl_line_buffer;
7305 const char *setting_name;
7307 const NameItem *valid_settings_main;
7308 const NameItem *valid_settings_slave;
7310 const char *slv_type;
7312 /* Try to get the setting from 'line' - setting_name.property */
7313 p1 = strchr (line, '.');
7315 while (p1 > line && !g_ascii_isspace (*p1))
7318 strv = g_strsplit (p1+1, ".", 2);
7320 valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
7322 /* Support autocompletion of slave-connection parameters
7323 * guessing the slave type from the setting name already
7324 * typed (or autocompleted) */
7325 if (nm_streq0 (strv[0], NM_SETTING_TEAM_PORT_SETTING_NAME))
7326 slv_type = "team-slave";
7327 else if (nm_streq0 (strv[0], NM_SETTING_BRIDGE_PORT_SETTING_NAME))
7328 slv_type = "bridge-slave";
7330 slv_type = "no-slave";
7331 valid_settings_slave = get_valid_settings_array (slv_type);
7333 setting_name = check_valid_name (strv[0],
7334 valid_settings_main,
7335 valid_settings_slave,
7337 setting = nmc_setting_new_for_name (setting_name);
7339 /* Else take the current setting, if any */
7340 setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
7344 valid_props = nmc_setting_get_valid_properties (setting);
7345 ret = nmc_rl_gen_func_basic (text, state, (const char **) valid_props);
7349 g_strfreev (valid_props);
7351 g_object_unref (setting);
7356 gen_compat_devices (const char *text, int state)
7359 const GPtrArray *devices;
7360 const char **compatible_devices;
7363 devices = nm_client_get_devices (nmc_tab_completion.nmc->client);
7364 if (devices->len == 0)
7367 compatible_devices = g_new (const char *, devices->len + 1);
7368 for (i = 0; i < devices->len; i++) {
7369 NMDevice *dev = g_ptr_array_index (devices, i);
7370 const char *ifname = nm_device_get_iface (dev);
7371 NMDevice *device = NULL;
7372 const char *spec_object = NULL;
7374 if (find_device_for_connection (nmc_tab_completion.nmc, nmc_tab_completion.connection,
7375 ifname, NULL, NULL, &device, &spec_object, NULL)) {
7376 compatible_devices[j++] = ifname;
7379 compatible_devices[j] = NULL;
7381 ret = nmc_rl_gen_func_basic (text, state, compatible_devices);
7383 g_free (compatible_devices);
7387 static const char **
7388 _create_vpn_array (const GPtrArray *connections, gboolean uuid)
7393 if (connections->len < 1)
7396 array = g_new (const char *, connections->len + 1);
7397 for (c = 0; c < connections->len; c++) {
7398 NMConnection *connection = NM_CONNECTION (connections->pdata[c]);
7399 const char *type = nm_connection_get_connection_type (connection);
7401 if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) == 0)
7402 array[idx++] = uuid ? nm_connection_get_uuid (connection) : nm_connection_get_id (connection);
7409 gen_vpn_uuids (const char *text, int state)
7411 const GPtrArray *connections = nm_cli.connections;
7415 if (connections->len < 1)
7418 uuids = _create_vpn_array (connections, TRUE);
7419 ret = nmc_rl_gen_func_basic (text, state, uuids);
7425 gen_vpn_ids (const char *text, int state)
7427 const GPtrArray *connections = nm_cli.connections;
7431 if (connections->len < 1)
7434 ids = _create_vpn_array (connections, FALSE);
7435 ret = nmc_rl_gen_func_basic (text, state, ids);
7440 static rl_compentry_func_t *
7441 get_gen_func_cmd_nmcli (const char *str)
7445 if (matches (str, "status-line") == 0)
7446 return gen_func_bool_values;
7447 if (matches (str, "save-confirmation") == 0)
7448 return gen_func_bool_values;
7449 if (matches (str, "show-secrets") == 0)
7450 return gen_func_bool_values;
7451 if (matches (str, "prompt-color") == 0)
7452 return gen_cmd_nmcli_prompt_color;
7457 * Helper function parsing line for completion.
7459 * line : the whole line to be parsed
7460 * end : the position of cursor in the line
7461 * cmd : command to match
7463 * cw_num : is set to the word number being completed (1, 2, 3, 4).
7464 * prev_word : returns the previous word (so that we have some context).
7466 * Returns TRUE when the first word of the 'line' matches 'cmd'.
7469 * line="rem" cmd="remove" -> TRUE cw_num=1
7470 * line="set con" cmd="set" -> TRUE cw_num=2
7471 * line="go ipv4.method" cmd="goto" -> TRUE cw_num=2
7472 * line=" des eth.mtu " cmd="describe" -> TRUE cw_num=3
7473 * line=" bla ipv4.method" cmd="goto" -> FALSE
7476 should_complete_cmd (const char *line, int end, const char *cmd,
7477 int *cw_num, char **prev_word)
7480 const char *word1, *word2, *word3;
7481 size_t n1, n2, n3, n4, n5, n6;
7482 gboolean word1_done, word2_done, word3_done;
7483 gboolean ret = FALSE;
7488 tmp = g_strdup (line);
7490 n1 = strspn (tmp, " \t");
7491 n2 = strcspn (tmp+n1, " \t\0") + n1;
7492 n3 = strspn (tmp+n2, " \t") + n2;
7493 n4 = strcspn (tmp+n3, " \t\0") + n3;
7494 n5 = strspn (tmp+n4, " \t") + n4;
7495 n6 = strcspn (tmp+n5, " \t\0") + n5;
7497 word1_done = end > n2;
7498 word2_done = end > n4;
7499 word3_done = end > n6;
7500 tmp[n2] = tmp[n4] = tmp[n6] = '\0';
7502 word1 = tmp[n1] ? tmp + n1 : NULL;
7503 word2 = tmp[n3] ? tmp + n3 : NULL;
7504 word3 = tmp[n5] ? tmp + n5 : NULL;
7511 } else if (!word2_done) {
7515 *prev_word = g_strdup (word1);
7516 } else if (!word3_done) {
7520 *prev_word = g_strdup (word2);
7525 *prev_word = g_strdup (word3);
7528 if (word1 && matches (word1, cmd) == 0)
7536 * extract_setting_and_property:
7537 * prompt: (in) (allow-none): prompt string, or NULL
7538 * line: (in) (allow-none): line, or NULL
7539 * setting: (out) (transfer full) (array zero-terminated=1):
7540 * return location for setting name
7541 * property: (out) (transfer full) (array zero-terminated=1):
7542 * return location for property name
7544 * Extract setting and property names from prompt and/or line.
7547 extract_setting_and_property (const char *prompt, const char *line,
7548 char **setting, char **property)
7554 /* prompt looks like this:
7555 "nmcli 802-1x>" or "nmcli 802-1x.pac-file>" */
7556 const char *p1, *p2, *dot;
7558 p1 = strchr (prompt, ' ');
7560 dot = strchr (++p1, '.');
7563 num1 = strcspn (p1, ".");
7564 num2 = strcspn (p2, ">");
7565 sett = num1 > 0 ? g_strndup (p1, num1) : NULL;
7566 prop = num2 > 0 ? g_strndup (p2, num2) : NULL;
7568 num1 = strcspn (p1, ">");
7569 sett = num1 > 0 ? g_strndup (p1, num1) : NULL;
7575 /* line looks like this:
7576 " set 802-1x.pac-file ..." or " set pac-file ..." */
7577 const char *p1, *p2, *dot;
7578 size_t n1, n2, n3, n4;
7579 size_t num1, num2, len;
7580 n1 = strspn (line, " \t"); /* white-space */
7581 n2 = strcspn (line+n1, " \t\0") + n1; /* command */
7582 n3 = strspn (line+n2, " \t") + n2; /* white-space */
7583 n4 = strcspn (line+n3, " \t\0") + n3; /* setting/property */
7587 dot = strchr (p1, '.');
7588 if (dot && dot < p1 + len) {
7590 num1 = strcspn (p1, ".");
7591 num2 = len > num1 + 1 ? len - num1 - 1 : 0;
7592 sett = num1 > 0 ? g_strndup (p1, num1) : sett;
7593 prop = num2 > 0 ? g_strndup (p2, num2) : prop;
7596 prop = len > 0 ? g_strndup (p1, len) : NULL;
7611 get_setting_and_property (const char *prompt, const char *line,
7612 NMSetting **setting_out, char**property_out)
7614 const NameItem *valid_settings_main;
7615 const NameItem *valid_settings_slave;
7616 const char *setting_name;
7617 NMSetting *setting = NULL;
7618 char *property = NULL;
7619 char *sett = NULL, *prop = NULL;
7620 NMSettingConnection *s_con;
7621 const char *s_type = NULL;
7624 extract_setting_and_property (prompt, line, &sett, &prop);
7626 /* Is this too much (and useless?) effort for an unlikely case? */
7627 s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
7629 s_type = nm_setting_connection_get_slave_type (s_con);
7630 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
7632 valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
7633 valid_settings_slave = get_valid_settings_array (slv_type);
7636 setting_name = check_valid_name (sett, valid_settings_main,
7637 valid_settings_slave, NULL);
7638 setting = nmc_setting_new_for_name (setting_name);
7640 setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
7642 if (setting && prop)
7643 property = is_property_valid (setting, prop, NULL);
7645 property = g_strdup (nmc_tab_completion.property);
7647 *setting_out = setting;
7648 *property_out = property;
7655 _get_and_check_property (const char *prompt,
7658 const char **array_multi,
7662 gboolean found = FALSE;
7664 extract_setting_and_property (prompt, line, NULL, &prop);
7667 found = !!nmc_string_is_valid (prop, array, NULL);
7668 if (array_multi && multi)
7669 *multi = !!nmc_string_is_valid (prop, array_multi, NULL);
7676 should_complete_files (const char *prompt, const char *line)
7678 const char *file_properties[] = {
7679 /* '802-1x' properties */
7686 "phase2-client-cert",
7688 "phase2-private-key",
7689 /* 'team' and 'team-port' properties */
7693 return _get_and_check_property (prompt, line, file_properties, NULL, NULL);
7697 should_complete_vpn_uuids (const char *prompt, const char *line)
7699 const char *uuid_properties[] = {
7700 /* 'connection' properties */
7704 return _get_and_check_property (prompt, line, uuid_properties, NULL, NULL);
7707 static const char **
7708 get_allowed_property_values (void)
7712 const char **avals = NULL;
7714 get_setting_and_property (rl_prompt, rl_line_buffer, &setting, &property);
7715 if (setting && property)
7716 avals = nmc_setting_get_property_allowed_values (setting, property);
7719 g_object_unref (setting);
7726 should_complete_property_values (const char *prompt, const char *line, gboolean *multi)
7728 /* properties allowing multiple values */
7729 const char *multi_props[] = {
7730 /* '802-1x' properties */
7731 NM_SETTING_802_1X_EAP,
7732 /* '802-11-wireless-security' properties */
7733 NM_SETTING_WIRELESS_SECURITY_PROTO,
7734 NM_SETTING_WIRELESS_SECURITY_PAIRWISE,
7735 NM_SETTING_WIRELESS_SECURITY_GROUP,
7736 /* 'bond' properties */
7737 NM_SETTING_BOND_OPTIONS,
7738 /* 'ethernet' properties */
7739 NM_SETTING_WIRED_S390_OPTIONS,
7742 _get_and_check_property (prompt, line, NULL, multi_props, multi);
7743 return get_allowed_property_values () != NULL;
7746 //FIXME: this helper should go to libnm later
7748 _setting_property_is_boolean (NMSetting *setting, const char *property_name)
7752 g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
7753 g_return_val_if_fail (property_name, FALSE);
7755 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);
7756 if (pspec && pspec->value_type == G_TYPE_BOOLEAN)
7762 should_complete_boolean (const char *prompt, const char *line)
7766 gboolean is_boolean = FALSE;
7768 get_setting_and_property (prompt, line, &setting, &property);
7769 if (setting && property)
7770 is_boolean = _setting_property_is_boolean (setting, property);
7773 g_object_unref (setting);
7780 gen_property_values (const char *text, int state)
7785 avals = get_allowed_property_values ();
7787 ret = nmc_rl_gen_func_basic (text, state, avals);
7792 extern int rl_complete_with_tilde_expansion;
7795 * Attempt to complete on the contents of TEXT. START and END show the
7796 * region of TEXT that contains the word to complete. We can use the
7797 * entire line in case we want to do some simple parsing. Return the
7798 * array of matches, or NULL if there aren't any.
7801 nmcli_editor_tab_completion (const char *text, int start, int end)
7803 char **match_array = NULL;
7804 const char *line = rl_line_buffer;
7805 rl_compentry_func_t *generator_func = NULL;
7811 /* Restore standard append character to space */
7812 rl_completion_append_character = ' ';
7814 /* Restore standard function for displaying matches */
7815 rl_completion_display_matches_hook = NULL;
7817 /* Disable default filename completion */
7818 rl_attempted_completion_over = 1;
7820 /* Enable tilde expansion when filenames are completed */
7821 rl_complete_with_tilde_expansion = 1;
7823 /* Filter out possible ANSI color escape sequences */
7824 prompt_tmp = nmc_filter_out_colors ((const char *) rl_prompt);
7826 /* Find the first non-space character */
7827 n1 = strspn (line, " \t");
7829 /* Choose the right generator function */
7830 if (strcmp (prompt_tmp, EDITOR_PROMPT_CON_TYPE) == 0)
7831 generator_func = gen_connection_types;
7832 else if (strcmp (prompt_tmp, EDITOR_PROMPT_SETTING) == 0)
7833 generator_func = gen_setting_names;
7834 else if (strcmp (prompt_tmp, EDITOR_PROMPT_PROPERTY) == 0)
7835 generator_func = gen_property_names;
7836 else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL))
7837 || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL)))
7838 generator_func = gen_func_bool_values_l10n;
7839 else if (g_str_has_prefix (prompt_tmp, "nmcli")) {
7840 if (!strchr (prompt_tmp, '.')) {
7841 int level = g_str_has_prefix (prompt_tmp, "nmcli>") ? 0 : 1;
7842 const char *dot = strchr (line, '.');
7845 /* Main menu - level 0,1 */
7847 generator_func = gen_nmcli_cmds_menu;
7849 if (should_complete_cmd (line, end, "goto", &num, NULL) && num <= 2) {
7850 if (level == 0 && (!dot || dot >= line + end))
7851 generator_func = gen_setting_names;
7853 generator_func = gen_property_names;
7854 } else if (should_complete_cmd (line, end, "set", &num, NULL)) {
7856 if (level == 0 && (!dot || dot >= line + end)) {
7857 generator_func = gen_setting_names;
7858 rl_completion_append_character = '.';
7860 generator_func = gen_property_names;
7861 } else if (num >= 3) {
7862 if (num == 3 && should_complete_files (NULL, line))
7863 rl_attempted_completion_over = 0;
7864 else if (should_complete_vpn_uuids (NULL, line)) {
7865 rl_completion_display_matches_hook = uuid_display_hook;
7866 generator_func = gen_vpn_uuids;
7867 } else if ( should_complete_property_values (NULL, line, &multi)
7868 && (num == 3 || multi)) {
7869 generator_func = gen_property_values;
7870 } else if (should_complete_boolean (NULL, line) && num == 3)
7871 generator_func = gen_func_bool_values;
7873 } else if ( ( should_complete_cmd (line, end, "remove", &num, NULL)
7874 || should_complete_cmd (line, end, "describe", &num, NULL))
7876 if (level == 0 && (!dot || dot >= line + end)) {
7877 generator_func = gen_setting_names;
7878 rl_completion_append_character = '.';
7880 generator_func = gen_property_names;
7881 } else if (should_complete_cmd (line, end, "nmcli", &num, &word)) {
7883 generator_func = gen_cmd_nmcli;
7885 generator_func = get_gen_func_cmd_nmcli (word);
7886 } else if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2) {
7887 if (level == 0 && (!dot || dot >= line + end))
7888 generator_func = gen_cmd_print0;
7890 generator_func = gen_property_names;
7891 } else if (should_complete_cmd (line, end, "verify", &num, NULL) && num <= 2) {
7892 generator_func = gen_cmd_verify0;
7893 } else if (should_complete_cmd (line, end, "activate", &num, NULL) && num <= 2) {
7894 generator_func = gen_compat_devices;
7895 } else if (should_complete_cmd (line, end, "save", &num, NULL) && num <= 2) {
7896 generator_func = gen_cmd_save;
7897 } else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
7898 generator_func = gen_nmcli_cmds_menu;
7901 /* Submenu - level 2 */
7903 generator_func = gen_nmcli_cmds_submenu;
7907 if ( should_complete_cmd (line, end, "add", &num, NULL)
7908 || should_complete_cmd (line, end, "set", &num, NULL)) {
7909 if (num <= 2 && should_complete_files (prompt_tmp, line))
7910 rl_attempted_completion_over = 0;
7911 else if (should_complete_vpn_uuids (prompt_tmp, line)) {
7912 rl_completion_display_matches_hook = uuid_display_hook;
7913 generator_func = gen_vpn_uuids;
7914 } else if ( should_complete_property_values (prompt_tmp, NULL, &multi)
7915 && (num <= 2 || multi)) {
7916 generator_func = gen_property_values;
7917 } else if (should_complete_boolean (prompt_tmp, NULL) && num <= 2)
7918 generator_func = gen_func_bool_values;
7920 if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2)
7921 generator_func = gen_cmd_print2;
7922 else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
7923 generator_func = gen_nmcli_cmds_submenu;
7929 match_array = rl_completion_matches (text, generator_func);
7931 g_free (prompt_tmp);
7936 #define NMCLI_EDITOR_HISTORY ".nmcli-history"
7939 load_history_cmds (const char *uuid)
7948 filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
7949 kf = g_key_file_new ();
7950 if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
7951 if (g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE))
7952 g_print ("Warning: %s parse error: %s\n", filename, err->message);
7953 g_key_file_free (kf);
7957 keys = g_key_file_get_keys (kf, uuid, NULL, NULL);
7958 for (i = 0; keys && keys[i]; i++) {
7959 line = g_key_file_get_string (kf, uuid, keys[i], NULL);
7965 g_key_file_free (kf);
7970 save_history_cmds (const char *uuid)
7972 HIST_ENTRY **hist = NULL;
7981 hist = history_list ();
7983 filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
7984 kf = g_key_file_new ();
7985 if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
7986 if ( !g_error_matches (err, G_FILE_ERROR, G_FILE_ERROR_NOENT)
7987 && !g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
7988 g_print ("Warning: %s parse error: %s\n", filename, err->message);
7989 g_key_file_free (kf);
7991 g_clear_error (&err);
7994 g_clear_error (&err);
7997 /* Remove previous history group and save new history entries */
7998 g_key_file_remove_group (kf, uuid, NULL);
7999 for (i = 0; hist[i]; i++)
8001 key = g_strdup_printf ("%zd", i);
8002 g_key_file_set_string (kf, uuid, key, hist[i]->line);
8006 /* Write history to file */
8007 data = g_key_file_to_data (kf, &len, NULL);
8009 g_file_set_contents (filename, data, len, NULL);
8012 g_key_file_free (kf);
8017 /*----------------------------------------------------------------------------*/
8020 editor_show_connection (NMConnection *connection, NmCli *nmc)
8022 nmc->print_output = NMC_PRINT_PRETTY;
8023 nmc->multiline_output = TRUE;
8024 nmc->escape_values = 0;
8026 /* Remove any previous data */
8027 nmc_empty_output_fields (nmc);
8029 nmc_connection_profile_details (connection, nmc, nmc->editor_show_secrets);
8033 editor_show_setting (NMSetting *setting, NmCli *nmc)
8035 g_print (_("['%s' setting values]\n"),
8036 nm_setting_get_name (setting));
8038 nmc->print_output = NMC_PRINT_NORMAL;
8039 nmc->multiline_output = TRUE;
8040 nmc->escape_values = 0;
8042 /* Remove any previous data */
8043 nmc_empty_output_fields (nmc);
8045 setting_details (setting, nmc, NULL, nmc->editor_show_secrets);
8049 NMC_EDITOR_MAIN_CMD_UNKNOWN = 0,
8050 NMC_EDITOR_MAIN_CMD_GOTO,
8051 NMC_EDITOR_MAIN_CMD_REMOVE,
8052 NMC_EDITOR_MAIN_CMD_SET,
8053 NMC_EDITOR_MAIN_CMD_DESCRIBE,
8054 NMC_EDITOR_MAIN_CMD_PRINT,
8055 NMC_EDITOR_MAIN_CMD_VERIFY,
8056 NMC_EDITOR_MAIN_CMD_SAVE,
8057 NMC_EDITOR_MAIN_CMD_ACTIVATE,
8058 NMC_EDITOR_MAIN_CMD_BACK,
8059 NMC_EDITOR_MAIN_CMD_HELP,
8060 NMC_EDITOR_MAIN_CMD_NMCLI,
8061 NMC_EDITOR_MAIN_CMD_QUIT,
8064 static NmcEditorMainCmd
8065 parse_editor_main_cmd (const char *cmd, char **cmd_arg)
8067 NmcEditorMainCmd editor_cmd = NMC_EDITOR_MAIN_CMD_UNKNOWN;
8070 vec = nmc_strsplit_set (cmd, " \t", 2);
8071 if (g_strv_length (vec) < 1) {
8074 return NMC_EDITOR_MAIN_CMD_UNKNOWN;
8077 if (matches (vec[0], "goto") == 0)
8078 editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO;
8079 else if (matches (vec[0], "remove") == 0)
8080 editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE;
8081 else if (matches (vec[0], "set") == 0)
8082 editor_cmd = NMC_EDITOR_MAIN_CMD_SET;
8083 else if (matches (vec[0], "describe") == 0)
8084 editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE;
8085 else if (matches (vec[0], "print") == 0)
8086 editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT;
8087 else if (matches (vec[0], "verify") == 0)
8088 editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY;
8089 else if (matches (vec[0], "save") == 0)
8090 editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE;
8091 else if (matches (vec[0], "activate") == 0)
8092 editor_cmd = NMC_EDITOR_MAIN_CMD_ACTIVATE;
8093 else if (matches (vec[0], "back") == 0)
8094 editor_cmd = NMC_EDITOR_MAIN_CMD_BACK;
8095 else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
8096 editor_cmd = NMC_EDITOR_MAIN_CMD_HELP;
8097 else if (matches (vec[0], "quit") == 0)
8098 editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT;
8099 else if (matches (vec[0], "nmcli") == 0)
8100 editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI;
8102 /* set pointer to command argument */
8104 *cmd_arg = vec[1] ? g_strstrip (g_strdup (vec[1])) : NULL;
8111 editor_main_usage (void)
8113 g_print ("------------------------------------------------------------------------------\n");
8114 /* TRANSLATORS: do not translate command names and keywords before ::
8115 * However, you should translate terms enclosed in <>.
8117 g_print (_("---[ Main menu ]---\n"
8118 "goto [<setting> | <prop>] :: go to a setting or property\n"
8119 "remove <setting>[.<prop>] | <prop> :: remove setting or reset property value\n"
8120 "set [<setting>.<prop> <value>] :: set property value\n"
8121 "describe [<setting>.<prop>] :: describe property\n"
8122 "print [all | <setting>[.<prop>]] :: print the connection\n"
8123 "verify [all | fix] :: verify the connection\n"
8124 "save [persistent|temporary] :: save the connection\n"
8125 "activate [<ifname>] [/<ap>|<nsp>] :: activate the connection\n"
8126 "back :: go one level up (back)\n"
8127 "help/? [<command>] :: print this help\n"
8128 "nmcli <conf-option> <value> :: nmcli configuration\n"
8129 "quit :: exit nmcli\n"));
8130 g_print ("------------------------------------------------------------------------------\n");
8134 editor_main_help (const char *command)
8137 editor_main_usage ();
8139 /* detailed command descriptions */
8140 NmcEditorMainCmd cmd = parse_editor_main_cmd (command, NULL);
8143 case NMC_EDITOR_MAIN_CMD_GOTO:
8144 g_print (_("goto <setting>[.<prop>] | <prop> :: enter setting/property for editing\n\n"
8145 "This command enters into a setting or property for editing it.\n\n"
8146 "Examples: nmcli> goto connection\n"
8147 " nmcli connection> goto secondaries\n"
8148 " nmcli> goto ipv4.addresses\n"));
8150 case NMC_EDITOR_MAIN_CMD_REMOVE:
8151 g_print (_("remove <setting>[.<prop>] :: remove setting or reset property value\n\n"
8152 "This command removes an entire setting from the connection, or if a property\n"
8153 "is given, resets that property to the default value.\n\n"
8154 "Examples: nmcli> remove wifi-sec\n"
8155 " nmcli> remove eth.mtu\n"));
8157 case NMC_EDITOR_MAIN_CMD_SET:
8158 g_print (_("set [<setting>.<prop> <value>] :: set property value\n\n"
8159 "This command sets property value.\n\n"
8160 "Example: nmcli> set con.id My connection\n"));
8162 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
8163 g_print (_("describe [<setting>.<prop>] :: describe property\n\n"
8164 "Shows property description. You can consult nm-settings(5) "
8165 "manual page to see all NM settings and properties.\n"));
8167 case NMC_EDITOR_MAIN_CMD_PRINT:
8168 g_print (_("print [all] :: print setting or connection values\n\n"
8169 "Shows current property or the whole connection.\n\n"
8170 "Example: nmcli ipv4> print all\n"));
8172 case NMC_EDITOR_MAIN_CMD_VERIFY:
8173 g_print (_("verify [all | fix] :: verify setting or connection validity\n\n"
8174 "Verifies whether the setting or connection is valid and can be saved later.\n"
8175 "It indicates invalid values on error. Some errors may be fixed automatically\n"
8176 "by 'fix' option.\n\n"
8177 "Examples: nmcli> verify\n"
8178 " nmcli> verify fix\n"
8179 " nmcli bond> verify\n"));
8181 case NMC_EDITOR_MAIN_CMD_SAVE:
8182 g_print (_("save [persistent|temporary] :: save the connection\n\n"
8183 "Sends the connection profile to NetworkManager that either will save it\n"
8184 "persistently, or will only keep it in memory. 'save' without an argument\n"
8185 "means 'save persistent'.\n"
8186 "Note that once you save the profile persistently those settings are saved\n"
8187 "across reboot or restart. Subsequent changes can also be temporary or\n"
8188 "persistent, but any temporary changes will not persist across reboot or\n"
8189 "restart. If you want to fully remove the persistent connection, the connection\n"
8190 "profile must be deleted.\n"));
8192 case NMC_EDITOR_MAIN_CMD_ACTIVATE:
8193 g_print (_("activate [<ifname>] [/<ap>|<nsp>] :: activate the connection\n\n"
8194 "Activates the connection.\n\n"
8195 "Available options:\n"
8196 "<ifname> - device the connection will be activated on\n"
8197 "/<ap>|<nsp> - AP (Wi-Fi) or NSP (WiMAX) (prepend with / when <ifname> is not specified)\n"));
8199 case NMC_EDITOR_MAIN_CMD_BACK:
8200 g_print (_("back :: go to upper menu level\n\n"));
8202 case NMC_EDITOR_MAIN_CMD_HELP:
8203 g_print (_("help/? [<command>] :: help for the nmcli commands\n\n"));
8205 case NMC_EDITOR_MAIN_CMD_NMCLI:
8206 g_print (_("nmcli [<conf-option> <value>] :: nmcli configuration\n\n"
8207 "Configures nmcli. The following options are available:\n"
8208 "status-line yes | no [default: no]\n"
8209 "save-confirmation yes | no [default: yes]\n"
8210 "show-secrets yes | no [default: no]\n"
8211 "prompt-color <color> | <0-8> [default: 0]\n"
8212 "%s" /* color table description */
8214 "Examples: nmcli> nmcli status-line yes\n"
8215 " nmcli> nmcli save-confirmation no\n"
8216 " nmcli> nmcli prompt-color 3\n"),
8218 " 1 = \33[30mblack\33[0m\n"
8219 " 2 = \33[31mred\33[0m\n"
8220 " 3 = \33[32mgreen\33[0m\n"
8221 " 4 = \33[33myellow\33[0m\n"
8222 " 5 = \33[34mblue\33[0m\n"
8223 " 6 = \33[35mmagenta\33[0m\n"
8224 " 7 = \33[36mcyan\33[0m\n"
8225 " 8 = \33[37mwhite\33[0m\n");
8227 case NMC_EDITOR_MAIN_CMD_QUIT:
8228 g_print (_("quit :: exit nmcli\n\n"
8229 "This command exits nmcli. When the connection being edited "
8230 "is not saved, the user is asked to confirm the action.\n"));
8233 g_print (_("Unknown command: '%s'\n"), command);
8240 NMC_EDITOR_SUB_CMD_UNKNOWN = 0,
8241 NMC_EDITOR_SUB_CMD_SET,
8242 NMC_EDITOR_SUB_CMD_ADD,
8243 NMC_EDITOR_SUB_CMD_CHANGE,
8244 NMC_EDITOR_SUB_CMD_REMOVE,
8245 NMC_EDITOR_SUB_CMD_DESCRIBE,
8246 NMC_EDITOR_SUB_CMD_PRINT,
8247 NMC_EDITOR_SUB_CMD_BACK,
8248 NMC_EDITOR_SUB_CMD_HELP,
8249 NMC_EDITOR_SUB_CMD_QUIT
8252 static NmcEditorSubCmd
8253 parse_editor_sub_cmd (const char *cmd, char **cmd_arg)
8255 NmcEditorSubCmd editor_cmd = NMC_EDITOR_SUB_CMD_UNKNOWN;
8258 vec = nmc_strsplit_set (cmd, " \t", 2);
8259 if (g_strv_length (vec) < 1) {
8262 return NMC_EDITOR_SUB_CMD_UNKNOWN;
8265 if (matches (vec[0], "set") == 0)
8266 editor_cmd = NMC_EDITOR_SUB_CMD_SET;
8267 else if (matches (vec[0], "add") == 0)
8268 editor_cmd = NMC_EDITOR_SUB_CMD_ADD;
8269 else if (matches (vec[0], "change") == 0)
8270 editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE;
8271 else if (matches (vec[0], "remove") == 0)
8272 editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE;
8273 else if (matches (vec[0], "describe") == 0)
8274 editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE;
8275 else if (matches (vec[0], "print") == 0)
8276 editor_cmd = NMC_EDITOR_SUB_CMD_PRINT;
8277 else if (matches (vec[0], "back") == 0)
8278 editor_cmd = NMC_EDITOR_SUB_CMD_BACK;
8279 else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
8280 editor_cmd = NMC_EDITOR_SUB_CMD_HELP;
8281 else if (matches (vec[0], "quit") == 0)
8282 editor_cmd = NMC_EDITOR_SUB_CMD_QUIT;
8284 /* set pointer to command argument */
8286 *cmd_arg = g_strdup (vec[1]);
8293 editor_sub_help (void)
8295 g_print ("------------------------------------------------------------------------------\n");
8296 /* TRANSLATORS: do not translate command names and keywords before ::
8297 * However, you should translate terms enclosed in <>.
8299 g_print (_("---[ Property menu ]---\n"
8300 "set [<value>] :: set new value\n"
8301 "add [<value>] :: add new option to the property\n"
8302 "change :: change current value\n"
8303 "remove [<index> | <option>] :: delete the value\n"
8304 "describe :: describe property\n"
8305 "print [setting | connection] :: print property (setting/connection) value(s)\n"
8306 "back :: go to upper level\n"
8307 "help/? [<command>] :: print this help or command description\n"
8308 "quit :: exit nmcli\n"));
8309 g_print ("------------------------------------------------------------------------------\n");
8313 editor_sub_usage (const char *command)
8319 /* detailed command descriptions */
8320 NmcEditorSubCmd cmdsub = parse_editor_sub_cmd (command, NULL);
8323 case NMC_EDITOR_SUB_CMD_SET:
8324 g_print (_("set [<value>] :: set new value\n\n"
8325 "This command sets provided <value> to this property\n"));
8327 case NMC_EDITOR_SUB_CMD_ADD:
8328 g_print (_("add [<value>] :: append new value to the property\n\n"
8329 "This command adds provided <value> to this property, if "
8330 "the property is of a container type. For single-valued "
8331 "properties the property value is replaced (same as 'set').\n"));
8333 case NMC_EDITOR_SUB_CMD_CHANGE:
8334 g_print (_("change :: change current value\n\n"
8335 "Displays current value and allows editing it.\n"));
8337 case NMC_EDITOR_SUB_CMD_REMOVE:
8338 g_print (_("remove [<value>|<index>|<option name>] :: delete the value\n\n"
8339 "Removes the property value. For single-valued properties, this sets the\n"
8340 "property back to its default value. For container-type properties, this removes\n"
8341 "all the values of that property, or you can specify an argument to remove just\n"
8342 "a single item or option. The argument is either a value or index of the item to\n"
8343 "remove, or an option name (for properties with named options).\n\n"
8344 "Examples: nmcli ipv4.dns> remove 8.8.8.8\n"
8345 " nmcli ipv4.dns> remove 2\n"
8346 " nmcli bond.options> remove downdelay\n\n"));
8348 case NMC_EDITOR_SUB_CMD_DESCRIBE:
8349 g_print (_("describe :: describe property\n\n"
8350 "Shows property description. You can consult nm-settings(5) "
8351 "manual page to see all NM settings and properties.\n"));
8353 case NMC_EDITOR_SUB_CMD_PRINT:
8354 g_print (_("print [property|setting|connection] :: print property (setting, connection) value(s)\n\n"
8355 "Shows property value. Providing an argument you can also display "
8356 "values for the whole setting or connection.\n"));
8358 case NMC_EDITOR_SUB_CMD_BACK:
8359 g_print (_("back :: go to upper menu level\n\n"));
8361 case NMC_EDITOR_SUB_CMD_HELP:
8362 g_print (_("help/? [<command>] :: help for nmcli commands\n\n"));
8364 case NMC_EDITOR_SUB_CMD_QUIT:
8365 g_print (_("quit :: exit nmcli\n\n"
8366 "This command exits nmcli. When the connection being edited "
8367 "is not saved, the user is asked to confirm the action.\n"));
8370 g_print (_("Unknown command: '%s'\n"), command);
8376 /*----------------------------------------------------------------------------*/
8380 NMActiveConnection *ac;
8384 static gboolean nmc_editor_cb_called;
8385 static GError *nmc_editor_error;
8386 static MonitorACInfo *nmc_editor_monitor_ac;
8387 static GMutex nmc_editor_mutex;
8388 static GCond nmc_editor_cond;
8391 * Store 'error' to shared 'nmc_editor_error' and monitoring info to
8392 * 'nmc_editor_monitor_ac' and signal the condition so that
8393 * the 'editor-thread' thread could process that.
8396 set_info_and_signal_editor_thread (GError *error, MonitorACInfo *monitor_ac_info)
8398 g_mutex_lock (&nmc_editor_mutex);
8399 nmc_editor_cb_called = TRUE;
8400 nmc_editor_error = error ? g_error_copy (error) : NULL;
8401 nmc_editor_monitor_ac = monitor_ac_info;
8402 g_cond_signal (&nmc_editor_cond);
8403 g_mutex_unlock (&nmc_editor_mutex);
8407 add_connection_editor_cb (GObject *client,
8408 GAsyncResult *result,
8411 NMRemoteConnection *connection;
8412 GError *error = NULL;
8414 connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
8415 set_info_and_signal_editor_thread (error, NULL);
8417 g_clear_object (&connection);
8418 g_clear_error (&error);
8422 update_connection_editor_cb (GObject *connection,
8423 GAsyncResult *result,
8426 GError *error = NULL;
8428 nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
8430 set_info_and_signal_editor_thread (error, NULL);
8431 g_clear_error (&error);
8435 progress_activation_editor_cb (gpointer user_data)
8437 MonitorACInfo *info = (MonitorACInfo *) user_data;
8438 NMDevice *device = info->device;
8439 NMActiveConnection *ac = info->ac;
8440 NMActiveConnectionState ac_state;
8441 NMDeviceState dev_state;
8446 ac_state = nm_active_connection_get_state (ac);
8447 dev_state = nm_device_get_state (device);
8449 nmc_terminal_show_progress (nmc_device_state_to_string (dev_state));
8451 if ( ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED
8452 || dev_state == NM_DEVICE_STATE_ACTIVATED) {
8453 nmc_terminal_erase_line ();
8454 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
8455 nm_object_get_path (NM_OBJECT (ac)));
8456 goto finish; /* we are done */
8457 } else if ( ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED
8458 || dev_state == NM_DEVICE_STATE_FAILED) {
8459 nmc_terminal_erase_line ();
8460 g_print (_("Error: Connection activation failed.\n"));
8461 goto finish; /* we are done */
8468 g_object_unref (device);
8470 g_object_unref (ac);
8475 activate_connection_editor_cb (GObject *client,
8476 GAsyncResult *result,
8479 ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
8480 NMDevice *device = info->device;
8481 const GPtrArray *ac_devs;
8482 MonitorACInfo *monitor_ac_info = NULL;
8483 NMActiveConnection *active;
8484 GError *error = NULL;
8486 active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
8490 ac_devs = nm_active_connection_get_devices (active);
8491 device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
8494 monitor_ac_info = g_malloc0 (sizeof (AddConnectionInfo));
8495 monitor_ac_info->device = g_object_ref (device);
8496 monitor_ac_info->ac = active;
8497 monitor_ac_info->monitor_id = g_timeout_add (120, progress_activation_editor_cb, monitor_ac_info);
8499 g_object_unref (active);
8501 set_info_and_signal_editor_thread (error, monitor_ac_info);
8502 g_clear_error (&error);
8505 /*----------------------------------------------------------------------------*/
8508 print_property_description (NMSetting *setting, const char *prop_name)
8512 desc = nmc_setting_get_property_desc (setting, prop_name);
8513 g_print ("\n=== [%s] ===\n%s\n", prop_name, desc);
8518 print_setting_description (NMSetting *setting)
8520 /* Show description of all properties */
8524 all_props = nmc_setting_get_valid_properties (setting);
8525 g_print (("<<< %s >>>\n"), nm_setting_get_name (setting));
8526 for (i = 0; all_props && all_props[i]; i++)
8527 print_property_description (setting, all_props[i]);
8528 g_strfreev (all_props);
8532 connection_remove_setting (NMConnection *connection, NMSetting *setting)
8536 g_return_val_if_fail (setting, FALSE);
8538 mandatory = is_setting_mandatory (connection, setting);
8540 nm_connection_remove_setting (connection, G_OBJECT_TYPE (setting));
8543 g_print (_("Error: setting '%s' is mandatory and cannot be removed.\n"),
8544 nm_setting_get_name (setting));
8549 editor_show_status_line (NMConnection *connection, gboolean dirty, gboolean temp)
8551 NMSettingConnection *s_con;
8552 const char *con_type, *con_id, *con_uuid;
8554 s_con = nm_connection_get_setting_connection (connection);
8556 con_type = nm_setting_connection_get_connection_type (s_con);
8557 con_id = nm_connection_get_id (connection);
8558 con_uuid = nm_connection_get_uuid (connection);
8560 /* TRANSLATORS: status line in nmcli connection editor */
8561 g_print (_("[ Type: %s | Name: %s | UUID: %s | Dirty: %s | Temp: %s ]\n"),
8562 con_type, con_id, con_uuid,
8563 dirty ? _("yes") : _("no"),
8564 temp ? _("yes") : _("no"));
8568 refresh_remote_connection (GWeakRef *weak, NMRemoteConnection **remote)
8572 g_return_val_if_fail (remote != NULL, FALSE);
8574 previous = (*remote != NULL);
8576 g_object_unref (*remote);
8577 *remote = g_weak_ref_get (weak);
8579 return (previous && !*remote);
8583 is_connection_dirty (NMConnection *connection, NMRemoteConnection *remote)
8585 return !nm_connection_compare (connection,
8586 remote ? NM_CONNECTION (remote) : NULL,
8587 NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS |
8588 NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP);
8595 gboolean want_quit = FALSE;
8597 answer = nmc_readline (_("The connection is not saved. "
8598 "Do you really want to quit? %s"),
8599 prompt_yes_no (FALSE, NULL));
8600 answer = answer ? g_strstrip (answer) : NULL;
8601 if (answer && matches (answer, WORD_LOC_YES) == 0)
8609 * Submenu for detailed property editing
8610 * Return: TRUE - continue; FALSE - should quit
8613 property_edit_submenu (NmCli *nmc,
8614 NMConnection *connection,
8615 NMRemoteConnection **rem_con,
8616 GWeakRef *rem_con_weak,
8617 NMSetting *curr_setting,
8618 const char *prop_name)
8620 NmcEditorSubCmd cmdsub;
8621 gboolean cmd_property_loop = TRUE;
8622 gboolean should_quit = FALSE;
8623 char *prop_val_user;
8624 gboolean set_result;
8625 GError *tmp_err = NULL;
8628 GValue prop_g_value = G_VALUE_INIT;
8629 gboolean temp_changes;
8632 /* Set global variable for use in TAB completion */
8633 nmc_tab_completion.property = prop_name;
8635 prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
8637 nm_setting_get_name (curr_setting), prop_name);
8639 while (cmd_property_loop) {
8640 char *cmd_property_user;
8641 char *cmd_property_arg;
8643 /* Get the remote connection again, it may have disapeared */
8644 removed = refresh_remote_connection (rem_con_weak, rem_con);
8646 g_print (_("The connection profile has been removed from another client. "
8647 "You may type 'save' in the main menu to restore it.\n"));
8649 /* Connection is dirty? (not saved or differs from the saved) */
8650 dirty = is_connection_dirty (connection, *rem_con);
8651 temp_changes = *rem_con ? nm_remote_connection_get_unsaved (*rem_con) : TRUE;
8652 if (nmc->editor_status_line)
8653 editor_show_status_line (connection, dirty, temp_changes);
8655 cmd_property_user = nmc_readline ("%s", prompt);
8656 if (!cmd_property_user || *cmd_property_user == '\0')
8658 cmdsub = parse_editor_sub_cmd (g_strstrip (cmd_property_user), &cmd_property_arg);
8661 case NMC_EDITOR_SUB_CMD_SET:
8662 case NMC_EDITOR_SUB_CMD_ADD:
8663 /* list, arrays,...: SET replaces the whole property value
8664 * ADD adds the new value(s)
8665 * single values: : both SET and ADD sets the new value
8667 if (!cmd_property_arg) {
8668 const char **avals = nmc_setting_get_property_allowed_values (curr_setting, prop_name);
8670 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
8671 g_print (_("Allowed values for '%s' property: %s\n"),
8672 prop_name, avals_str);
8675 prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
8677 prop_val_user = g_strdup (cmd_property_arg);
8679 /* nmc_setting_set_property() only adds new value, thus we have to
8680 * remove the original value and save it for error cases.
8682 if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
8683 nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
8684 nmc_property_set_default_value (curr_setting, prop_name);
8687 set_result = nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err);
8688 g_free (prop_val_user);
8690 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
8691 g_clear_error (&tmp_err);
8692 if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
8693 /* Block change signals and restore original value */
8694 g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8695 nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
8696 g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8699 if (G_IS_VALUE (&prop_g_value))
8700 g_value_unset (&prop_g_value);
8703 case NMC_EDITOR_SUB_CMD_CHANGE:
8704 rl_startup_hook = nmc_rl_set_deftext;
8705 nmc_rl_pre_input_deftext = nmc_setting_get_property_parsable (curr_setting, prop_name, NULL);
8706 prop_val_user = nmc_readline (_("Edit '%s' value: "), prop_name);
8708 nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
8709 nmc_property_set_default_value (curr_setting, prop_name);
8711 if (!nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err)) {
8712 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
8713 g_clear_error (&tmp_err);
8714 g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8715 nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
8716 g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8718 g_free (prop_val_user);
8719 if (G_IS_VALUE (&prop_g_value))
8720 g_value_unset (&prop_g_value);
8723 case NMC_EDITOR_SUB_CMD_REMOVE:
8724 if (cmd_property_arg) {
8725 unsigned long val_int = G_MAXUINT32;
8726 char *option = NULL;
8728 if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int))
8729 option = g_strdup (cmd_property_arg);
8731 if (!nmc_setting_remove_property_option (curr_setting, prop_name,
8732 option ? g_strstrip (option) : NULL,
8735 g_print (_("Error: %s\n"), tmp_err->message);
8736 g_clear_error (&tmp_err);
8740 if (!nmc_setting_reset_property (curr_setting, prop_name, &tmp_err)) {
8741 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
8743 g_clear_error (&tmp_err);
8748 case NMC_EDITOR_SUB_CMD_DESCRIBE:
8749 /* Show property description */
8750 print_property_description (curr_setting, prop_name);
8753 case NMC_EDITOR_SUB_CMD_PRINT:
8754 /* Print current connection settings/properties */
8755 if (cmd_property_arg) {
8756 if (matches (cmd_property_arg, "setting") == 0)
8757 editor_show_setting (curr_setting, nmc);
8758 else if ( matches (cmd_property_arg, "connection") == 0
8759 || matches (cmd_property_arg, "all") == 0)
8760 editor_show_connection (connection, nmc);
8762 g_print (_("Unknown command argument: '%s'\n"), cmd_property_arg);
8764 char *prop_val = nmc_setting_get_property (curr_setting, prop_name, NULL);
8765 g_print ("%s: %s\n", prop_name, prop_val);
8770 case NMC_EDITOR_SUB_CMD_BACK:
8771 /* Set global variable for use in TAB completion */
8772 nmc_tab_completion.property = NULL;
8773 cmd_property_loop = FALSE;
8776 case NMC_EDITOR_SUB_CMD_HELP:
8777 editor_sub_usage (cmd_property_arg);
8780 case NMC_EDITOR_SUB_CMD_QUIT:
8781 if (is_connection_dirty (connection, *rem_con)) {
8782 if (confirm_quit ()) {
8783 cmd_property_loop = FALSE;
8784 should_quit = TRUE; /* we will quit nmcli */
8787 cmd_property_loop = FALSE;
8788 should_quit = TRUE; /* we will quit nmcli */
8792 case NMC_EDITOR_SUB_CMD_UNKNOWN:
8794 g_print (_("Unknown command: '%s'\n"), cmd_property_user);
8797 g_free (cmd_property_user);
8798 g_free (cmd_property_arg);
8802 return !should_quit;
8806 * Split 'str' in the following format: [[[setting.]property] [value]]
8807 * and return the components in 'setting', 'property' and 'value'
8808 * Use g_free() to deallocate the returned strings.
8811 split_editor_main_cmd_args (const char *str, char **setting, char **property, char **value)
8813 char **args, **items;
8818 args = nmc_strsplit_set (str, " \t", 2);
8820 items = nmc_strsplit_set (args[0], ".", 2);
8821 if (g_strv_length (items) == 2) {
8823 *setting = g_strdup (items[0]);
8825 *property = g_strdup (items[1]);
8828 *property = g_strdup (items[0]);
8832 if (value && args[1])
8833 *value = g_strstrip (g_strdup (args[1]));
8839 create_setting_by_name (const char *name, const NameItem *valid_settings_main, const NameItem *valid_settings_slave)
8841 const char *setting_name;
8842 NMSetting *setting = NULL;
8844 /* Get a valid setting name */
8845 setting_name = check_valid_name (name, valid_settings_main, valid_settings_slave, NULL);
8848 setting = nmc_setting_new_for_name (setting_name);
8850 return NULL; /* This should really not happen */
8851 nmc_setting_custom_init (setting);
8857 ask_check_setting (const char *arg,
8858 const NameItem *valid_settings_main,
8859 const NameItem *valid_settings_slave,
8860 const char *valid_settings_str)
8862 char *setting_name_user;
8863 const char *setting_name;
8867 g_print (_("Available settings: %s\n"), valid_settings_str);
8868 setting_name_user = nmc_readline (EDITOR_PROMPT_SETTING);
8870 setting_name_user = g_strdup (arg);
8872 if (setting_name_user)
8873 g_strstrip (setting_name_user);
8875 if (!(setting_name = check_valid_name (setting_name_user,
8876 valid_settings_main,
8877 valid_settings_slave,
8879 g_print (_("Error: invalid setting name; %s\n"), err->message);
8880 g_clear_error (&err);
8882 g_free (setting_name_user);
8883 return setting_name;
8887 ask_check_property (const char *arg,
8888 const char **valid_props,
8889 const char *valid_props_str)
8891 char *prop_name_user;
8892 const char *prop_name;
8893 GError *tmp_err = NULL;
8896 g_print (_("Available properties: %s\n"), valid_props_str);
8897 prop_name_user = nmc_readline (EDITOR_PROMPT_PROPERTY);
8899 g_strstrip (prop_name_user);
8901 prop_name_user = g_strdup (arg);
8903 if (!(prop_name = nmc_string_is_valid (prop_name_user, valid_props, &tmp_err))) {
8904 g_print (_("Error: property %s\n"), tmp_err->message);
8905 g_clear_error (&tmp_err);
8907 g_free (prop_name_user);
8911 /* Copy timestamp from src do dst */
8913 update_connection_timestamp (NMConnection *src, NMConnection *dst)
8915 NMSettingConnection *s_con_src, *s_con_dst;
8917 s_con_src = nm_connection_get_setting_connection (src);
8918 s_con_dst = nm_connection_get_setting_connection (dst);
8919 if (s_con_src && s_con_dst) {
8920 guint64 timestamp = nm_setting_connection_get_timestamp (s_con_src);
8921 g_object_set (s_con_dst, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL);
8926 confirm_connection_saving (NMConnection *local, NMConnection *remote)
8928 NMSettingConnection *s_con_loc, *s_con_rem;
8929 gboolean ac_local, ac_remote;
8930 gboolean confirmed = TRUE;
8932 s_con_loc = nm_connection_get_setting_connection (local);
8933 g_assert (s_con_loc);
8934 ac_local = nm_setting_connection_get_autoconnect (s_con_loc);
8937 s_con_rem = nm_connection_get_setting_connection (remote);
8938 g_assert (s_con_rem);
8939 ac_remote = nm_setting_connection_get_autoconnect (s_con_rem);
8943 if (ac_local && !ac_remote) {
8945 answer = nmc_readline (_("Saving the connection with 'autoconnect=yes'. "
8946 "That might result in an immediate activation of the connection.\n"
8947 "Do you still want to save? %s"), prompt_yes_no (TRUE, NULL));
8948 answer = answer ? g_strstrip (answer) : NULL;
8949 if (!answer || matches (answer, WORD_LOC_YES) == 0)
8961 NMSetting *curr_setting;
8963 char *valid_props_str;
8964 } NmcEditorMenuContext;
8967 menu_switch_to_level0 (NmCli *nmc,
8968 NmcEditorMenuContext *menu_ctx,
8970 NmcTermColor prompt_color)
8972 menu_ctx->level = 0;
8973 g_free (menu_ctx->main_prompt);
8974 menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL, "%s", prompt);
8975 menu_ctx->curr_setting = NULL;
8976 g_strfreev (menu_ctx->valid_props);
8977 menu_ctx->valid_props = NULL;
8978 g_free (menu_ctx->valid_props_str);
8979 menu_ctx->valid_props_str = NULL;
8983 menu_switch_to_level1 (NmCli *nmc,
8984 NmcEditorMenuContext *menu_ctx,
8986 const char *setting_name,
8987 NmcTermColor prompt_color)
8989 menu_ctx->level = 1;
8990 g_free (menu_ctx->main_prompt);
8991 menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL,
8992 "nmcli %s> ", setting_name);
8993 menu_ctx->curr_setting = setting;
8994 g_strfreev (menu_ctx->valid_props);
8995 menu_ctx->valid_props = nmc_setting_get_valid_properties (menu_ctx->curr_setting);
8996 g_free (menu_ctx->valid_props_str);
8997 menu_ctx->valid_props_str = g_strjoinv (", ", menu_ctx->valid_props);
9001 editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type)
9003 NMSettingConnection *s_con;
9004 NMRemoteConnection *rem_con;
9005 NMRemoteConnection *con_tmp;
9006 GWeakRef weak = { { NULL } };
9008 NmcEditorMainCmd cmd;
9010 gboolean cmd_loop = TRUE;
9011 char *cmd_arg = NULL;
9012 char *cmd_arg_s, *cmd_arg_p, *cmd_arg_v;
9013 const char *BASE_PROMPT = "nmcli> ";
9014 const NameItem *valid_settings_main = NULL;
9015 const NameItem *valid_settings_slave = NULL;
9016 char *valid_settings_str = NULL;
9017 const char *s_type = NULL;
9019 AddConnectionInfo *info = NULL;
9021 gboolean temp_changes;
9022 GError *err1 = NULL;
9023 NmcEditorMenuContext menu_ctx;
9025 s_con = nm_connection_get_setting_connection (connection);
9027 s_type = nm_setting_connection_get_slave_type (s_con);
9028 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
9030 valid_settings_main = get_valid_settings_array (connection_type);
9031 valid_settings_slave = get_valid_settings_array (slv_type);
9034 valid_settings_str = get_valid_options_string (valid_settings_main, valid_settings_slave);
9035 g_print (_("You may edit the following settings: %s\n"), valid_settings_str);
9038 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9040 menu_ctx.curr_setting = NULL;
9041 menu_ctx.valid_props = NULL;
9042 menu_ctx.valid_props_str = NULL;
9044 /* Get remote connection */
9045 con_tmp = nm_client_get_connection_by_uuid (nmc->client,
9046 nm_connection_get_uuid (connection));
9047 g_weak_ref_init (&weak, con_tmp);
9048 rem_con = g_weak_ref_get (&weak);
9051 /* Connection is dirty? (not saved or differs from the saved) */
9052 dirty = is_connection_dirty (connection, rem_con);
9053 temp_changes = rem_con ? nm_remote_connection_get_unsaved (rem_con) : TRUE;
9054 if (nmc->editor_status_line)
9055 editor_show_status_line (connection, dirty, temp_changes);
9057 /* Read user input */
9058 cmd_user = nmc_readline ("%s", menu_ctx.main_prompt);
9060 /* Get the remote connection again, it may have disapeared */
9061 removed = refresh_remote_connection (&weak, &rem_con);
9063 g_print (_("The connection profile has been removed from another client. "
9064 "You may type 'save' to restore it.\n"));
9066 if (!cmd_user || *cmd_user == '\0')
9068 cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg);
9073 split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v);
9075 case NMC_EDITOR_MAIN_CMD_SET:
9076 /* Set property value */
9078 if (menu_ctx.level == 1) {
9079 const char *prop_name;
9080 char *prop_val_user = NULL;
9082 GError *tmp_err = NULL;
9084 prop_name = ask_check_property (cmd_arg,
9085 (const char **) menu_ctx.valid_props,
9086 menu_ctx.valid_props_str);
9090 avals = nmc_setting_get_property_allowed_values (menu_ctx.curr_setting, prop_name);
9092 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
9093 g_print (_("Allowed values for '%s' property: %s\n"),
9094 prop_name, avals_str);
9097 prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
9099 /* Set property value */
9100 if (!nmc_setting_set_property (menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) {
9101 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
9102 g_clear_error (&tmp_err);
9105 g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
9106 g_print (_("use 'goto <setting>' first, or 'set <setting>.<property>'\n"));
9109 NMSetting *ss = NULL;
9110 gboolean created_ss = FALSE;
9112 GError *tmp_err = NULL;
9115 /* setting provided as "setting.property" */
9116 ss = is_setting_valid (connection, valid_settings_main, valid_settings_slave, cmd_arg_s);
9118 ss = create_setting_by_name (cmd_arg_s, valid_settings_main, valid_settings_slave);
9120 g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9121 cmd_arg_s, valid_settings_str);
9127 if (menu_ctx.curr_setting)
9128 ss = menu_ctx.curr_setting;
9130 g_print (_("Error: missing setting for '%s' property\n"), cmd_arg_p);
9135 prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9137 g_print (_("Error: invalid property: %s\n"), tmp_err->message);
9138 g_clear_error (&tmp_err);
9140 g_object_unref (ss);
9148 const char **avals = nmc_setting_get_property_allowed_values (ss, prop_name);
9150 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
9151 g_print (_("Allowed values for '%s' property: %s\n"),
9152 prop_name, avals_str);
9155 cmd_arg_v = nmc_readline (_("Enter '%s' value: "), prop_name);
9158 /* Set property value */
9159 if (!nmc_setting_set_property (ss, prop_name, cmd_arg_v, &tmp_err)) {
9160 g_print (_("Error: failed to set '%s' property: %s\n"),
9161 prop_name, tmp_err->message);
9162 g_clear_error (&tmp_err);
9166 nm_connection_add_setting (connection, ss);
9171 case NMC_EDITOR_MAIN_CMD_GOTO:
9172 /* cmd_arg_s != NULL means 'setting.property' argument */
9173 if (menu_ctx.level == 0 || cmd_arg_s) {
9174 /* in top level - no setting selected yet */
9175 const char *setting_name;
9177 const char *user_arg = cmd_arg_s ? cmd_arg_s : cmd_arg_p;
9179 setting_name = ask_check_setting (user_arg,
9180 valid_settings_main,
9181 valid_settings_slave,
9182 valid_settings_str);
9186 setting = nm_connection_get_setting_by_name (connection, setting_name);
9188 setting = nmc_setting_new_for_name (setting_name);
9190 g_print (_("Error: unknown setting '%s'\n"), setting_name);
9193 nmc_setting_custom_init (setting);
9194 nm_connection_add_setting (connection, setting);
9196 /* Set global variable for use in TAB completion */
9197 nmc_tab_completion.setting = setting;
9199 /* Switch to level 1 */
9200 menu_switch_to_level1 (nmc, &menu_ctx, setting, setting_name, nmc->editor_prompt_color);
9203 g_print (_("You may edit the following properties: %s\n"), menu_ctx.valid_props_str);
9207 if (menu_ctx.level == 1 || cmd_arg_s) {
9208 /* level 1 - setting selected */
9209 const char *prop_name;
9211 prop_name = ask_check_property (cmd_arg_p,
9212 (const char **) menu_ctx.valid_props,
9213 menu_ctx.valid_props_str);
9217 /* submenu - level 2 - editing properties */
9218 cmd_loop = property_edit_submenu (nmc,
9222 menu_ctx.curr_setting,
9227 case NMC_EDITOR_MAIN_CMD_REMOVE:
9228 /* Remove setting from connection, or delete value of a property */
9230 if (menu_ctx.level == 1) {
9231 GError *tmp_err = NULL;
9232 const char *prop_name;
9234 prop_name = ask_check_property (cmd_arg,
9235 (const char **) menu_ctx.valid_props,
9236 menu_ctx.valid_props_str);
9240 /* Delete property value */
9241 if (!nmc_setting_reset_property (menu_ctx.curr_setting, prop_name, &tmp_err)) {
9242 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
9244 g_clear_error (&tmp_err);
9247 g_print (_("Error: no argument given; valid are [%s]\n"), valid_settings_str);
9249 NMSetting *ss = NULL;
9253 /* cmd_arg_s != NULL means argument is "setting.property" */
9254 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
9255 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9257 ss = is_setting_valid (connection,
9258 valid_settings_main,
9259 valid_settings_slave,
9262 if (check_valid_name (user_s,
9263 valid_settings_main,
9264 valid_settings_slave,
9266 g_print (_("Setting '%s' is not present in the connection.\n"),
9269 g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9270 user_s, valid_settings_str);
9274 ss = menu_ctx.curr_setting;
9277 /* Remove setting from the connection */
9278 connection_remove_setting (connection, ss);
9279 if (ss == menu_ctx.curr_setting) {
9280 /* If we removed the setting we are in, go up */
9281 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9282 nmc_tab_completion.setting = NULL; /* for TAB completion */
9285 GError *tmp_err = NULL;
9286 char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9288 /* Delete property value */
9289 if (!nmc_setting_reset_property (ss, prop_name, &tmp_err)) {
9290 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
9292 g_clear_error (&tmp_err);
9295 /* If the string is not a property, try it as a setting */
9297 s_tmp = is_setting_valid (connection,
9298 valid_settings_main,
9299 valid_settings_slave,
9302 /* Remove setting from the connection */
9303 connection_remove_setting (connection, s_tmp);
9304 /* coverity[copy_paste_error] - suppress Coverity COPY_PASTE_ERROR defect */
9305 if (ss == menu_ctx.curr_setting) {
9306 /* If we removed the setting we are in, go up */
9307 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9308 nmc_tab_completion.setting = NULL; /* for TAB completion */
9311 g_print (_("Error: %s properties, nor it is a setting name.\n"),
9313 g_clear_error (&tmp_err);
9320 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
9321 /* Print property description */
9323 if (menu_ctx.level == 1) {
9324 const char *prop_name;
9326 prop_name = ask_check_property (cmd_arg,
9327 (const char **) menu_ctx.valid_props,
9328 menu_ctx.valid_props_str);
9332 /* Show property description */
9333 print_property_description (menu_ctx.curr_setting, prop_name);
9335 g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
9336 g_print (_("use 'goto <setting>' first, or 'describe <setting>.<property>'\n"));
9339 NMSetting *ss = NULL;
9340 gboolean unref_ss = FALSE;
9344 /* cmd_arg_s != NULL means argument is "setting.property" */
9345 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
9346 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9348 ss = is_setting_valid (connection,
9349 valid_settings_main,
9350 valid_settings_slave,
9353 ss = create_setting_by_name (user_s,
9354 valid_settings_main,
9355 valid_settings_slave);
9357 g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9358 user_s, valid_settings_str);
9364 ss = menu_ctx.curr_setting;
9367 /* Show description for all properties */
9368 print_setting_description (ss);
9370 GError *tmp_err = NULL;
9371 char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9373 /* Show property description */
9374 print_property_description (ss, prop_name);
9376 /* If the string is not a property, try it as a setting */
9378 s_tmp = is_setting_valid (connection,
9379 valid_settings_main,
9380 valid_settings_slave,
9383 print_setting_description (s_tmp);
9385 g_print (_("Error: invalid property: %s, "
9386 "neither a valid setting name.\n"),
9388 g_clear_error (&tmp_err);
9393 g_object_unref (ss);
9397 case NMC_EDITOR_MAIN_CMD_PRINT:
9398 /* Print current connection settings/properties */
9400 if (strcmp (cmd_arg, "all") == 0)
9401 editor_show_connection (connection, nmc);
9403 NMSetting *ss = NULL;
9404 gboolean whole_setting;
9407 /* cmd_arg_s != NULL means argument is "setting.property" */
9408 whole_setting = !cmd_arg_s && !menu_ctx.curr_setting;
9409 user_s = whole_setting ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9412 s_name = check_valid_name (user_s,
9413 valid_settings_main,
9414 valid_settings_slave,
9417 g_print (_("Error: unknown setting: '%s'\n"), user_s);
9420 ss = nm_connection_get_setting_by_name (connection, s_name);
9422 g_print (_("Error: '%s' setting not present in the connection\n"), s_name);
9426 ss = menu_ctx.curr_setting;
9428 if (whole_setting) {
9429 /* Print the whole setting */
9430 editor_show_setting (ss, nmc);
9433 char *prop_name = is_property_valid (ss, cmd_arg_p, &err);
9435 /* Print one property */
9436 char *prop_val = nmc_setting_get_property (ss, prop_name, NULL);
9437 g_print ("%s.%s: %s\n", nm_setting_get_name (ss),prop_name , prop_val);
9440 /* If the string is not a property, try it as a setting */
9442 s_tmp = is_setting_valid (connection,
9443 valid_settings_main,
9444 valid_settings_slave,
9447 /* Print the whole setting */
9448 editor_show_setting (s_tmp, nmc);
9450 g_print (_("Error: invalid property: %s%s\n"),
9452 cmd_arg_s ? "" : _(", neither a valid setting name"));
9453 g_clear_error (&err);
9459 if (menu_ctx.curr_setting)
9460 editor_show_setting (menu_ctx.curr_setting, nmc);
9462 editor_show_connection (connection, nmc);
9466 case NMC_EDITOR_MAIN_CMD_VERIFY:
9467 /* Verify current setting or the whole connection */
9468 if (cmd_arg && strcmp (cmd_arg, "all") && strcmp (cmd_arg, "fix")) {
9469 g_print (_("Invalid verify option: %s\n"), cmd_arg);
9473 if ( menu_ctx.curr_setting
9474 && (!cmd_arg || strcmp (cmd_arg, "all") != 0)) {
9475 GError *tmp_err = NULL;
9476 (void) nm_setting_verify (menu_ctx.curr_setting, NULL, &tmp_err);
9477 g_print (_("Verify setting '%s': %s\n"),
9478 nm_setting_get_name (menu_ctx.curr_setting),
9479 tmp_err ? tmp_err->message : "OK");
9480 g_clear_error (&tmp_err);
9482 GError *tmp_err = NULL;
9483 gboolean valid, modified;
9484 gboolean fixed = TRUE;
9486 valid = nm_connection_verify (connection, &tmp_err);
9487 if (!valid && (g_strcmp0 (cmd_arg, "fix") == 0)) {
9488 /* Try to fix normalizable errors */
9489 g_clear_error (&tmp_err);
9490 fixed = nm_connection_normalize (connection, NULL, &modified, &tmp_err);
9492 g_print (_("Verify connection: %s\n"),
9493 tmp_err ? tmp_err->message : "OK");
9495 g_print (_("The error cannot be fixed automatically.\n"));
9496 g_clear_error (&tmp_err);
9500 case NMC_EDITOR_MAIN_CMD_SAVE:
9501 /* Save the connection */
9502 if (nm_connection_verify (connection, &err1)) {
9503 gboolean persistent = TRUE;
9505 /* parse argument */
9507 if (matches (cmd_arg, "temporary") == 0)
9509 else if (matches (cmd_arg, "persistent") == 0)
9512 g_print (_("Error: invalid argument '%s'\n"), cmd_arg);
9517 /* Ask for save confirmation if the connection changes to autoconnect=yes */
9518 if (nmc->editor_save_confirmation)
9519 if (!confirm_connection_saving (connection, NM_CONNECTION (rem_con)))
9523 /* Tell the settings service to add the new connection */
9524 info = g_malloc0 (sizeof (AddConnectionInfo));
9526 info->con_name = g_strdup (nm_connection_get_id (connection));
9527 add_new_connection (persistent,
9530 add_connection_editor_cb,
9533 /* Save/update already saved (existing) connection */
9534 nm_connection_replace_settings_from_connection (NM_CONNECTION (rem_con),
9536 update_connection (persistent, rem_con, update_connection_editor_cb, NULL);
9539 g_mutex_lock (&nmc_editor_mutex);
9540 //FIXME: add also a timeout for cases the callback is not called
9541 while (!nmc_editor_cb_called)
9542 g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
9544 if (nmc_editor_error) {
9545 g_print (_("Error: Failed to save '%s' (%s) connection: %s\n"),
9546 nm_connection_get_id (connection),
9547 nm_connection_get_uuid (connection),
9548 nmc_editor_error->message);
9549 g_error_free (nmc_editor_error);
9552 _("Connection '%s' (%s) successfully saved.\n") :
9553 _("Connection '%s' (%s) successfully updated.\n"),
9554 nm_connection_get_id (connection),
9555 nm_connection_get_uuid (connection));
9557 con_tmp = nm_client_get_connection_by_uuid (nmc->client,
9558 nm_connection_get_uuid (connection));
9559 g_weak_ref_set (&weak, con_tmp);
9560 refresh_remote_connection (&weak, &rem_con);
9562 /* Replace local connection with the remote one to be sure they are equal.
9563 * This mitigates problems with plugins not preserving some properties or
9564 * adding ipv{4,6} settings when not present.
9567 char *s_name = NULL;
9568 if (menu_ctx.curr_setting)
9569 s_name = g_strdup (nm_setting_get_name (menu_ctx.curr_setting));
9571 /* Update settings in the local connection */
9572 nm_connection_replace_settings_from_connection (connection,
9573 NM_CONNECTION (con_tmp));
9575 /* Also update setting for menu context and TAB-completion */
9576 menu_ctx.curr_setting = s_name ? nm_connection_get_setting_by_name (connection, s_name) : NULL;
9577 nmc_tab_completion.setting = menu_ctx.curr_setting;
9582 nmc_editor_cb_called = FALSE;
9583 nmc_editor_error = NULL;
9584 g_mutex_unlock (&nmc_editor_mutex);
9586 g_print (_("Error: connection verification failed: %s\n"),
9587 err1 ? err1->message : _("(unknown error)"));
9588 g_print (_("You may try running 'verify fix' to fix errors.\n"));
9591 g_clear_error (&err1);
9594 case NMC_EDITOR_MAIN_CMD_ACTIVATE:
9596 GError *tmp_err = NULL;
9597 const char *ifname = cmd_arg_p;
9598 const char *ap_nsp = cmd_arg_v;
9600 /* When only AP/NSP is specified it is prepended with '/' */
9602 if (ifname && ifname[0] == '/') {
9603 ap_nsp = ifname + 1;
9607 ap_nsp = ap_nsp && ap_nsp[0] == '/' ? ap_nsp + 1 : ap_nsp;
9609 if (is_connection_dirty (connection, rem_con)) {
9610 g_print (_("Error: connection is not saved. Type 'save' first.\n"));
9613 if (!nm_connection_verify (NM_CONNECTION (rem_con), &tmp_err)) {
9614 g_print (_("Error: connection is not valid: %s\n"), tmp_err->message);
9615 g_clear_error (&tmp_err);
9619 nmc->nowait_flag = FALSE;
9621 nmc->print_output = NMC_PRINT_PRETTY;
9622 if (!nmc_activate_connection (nmc, NM_CONNECTION (rem_con), ifname, ap_nsp, ap_nsp, NULL,
9623 activate_connection_editor_cb, &tmp_err)) {
9624 g_print (_("Error: Cannot activate connection: %s.\n"), tmp_err->message);
9625 g_clear_error (&tmp_err);
9629 g_mutex_lock (&nmc_editor_mutex);
9630 while (!nmc_editor_cb_called)
9631 g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
9633 if (nmc_editor_error) {
9634 g_print (_("Error: Failed to activate '%s' (%s) connection: %s\n"),
9635 nm_connection_get_id (connection),
9636 nm_connection_get_uuid (connection),
9637 nmc_editor_error->message);
9638 g_error_free (nmc_editor_error);
9640 g_print (_("Monitoring connection activation (press any key to continue)\n"));
9641 nmc_get_user_input ("");
9644 if (nmc_editor_monitor_ac) {
9645 if (nmc_editor_monitor_ac->monitor_id)
9646 g_source_remove (nmc_editor_monitor_ac->monitor_id);
9647 g_free (nmc_editor_monitor_ac);
9649 nmc_editor_cb_called = FALSE;
9650 nmc_editor_error = NULL;
9651 nmc_editor_monitor_ac = NULL;
9652 g_mutex_unlock (&nmc_editor_mutex);
9654 /* Update timestamp in local connection */
9655 update_connection_timestamp (NM_CONNECTION (rem_con), connection);
9660 case NMC_EDITOR_MAIN_CMD_BACK:
9661 /* Go back (up) an the menu */
9662 if (menu_ctx.level == 1) {
9663 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9664 nmc_tab_completion.setting = NULL; /* for TAB completion */
9668 case NMC_EDITOR_MAIN_CMD_HELP:
9669 /* Print command help */
9670 editor_main_help (cmd_arg);
9673 case NMC_EDITOR_MAIN_CMD_NMCLI:
9674 if (cmd_arg_p && matches (cmd_arg_p, "status-line") == 0) {
9675 GError *tmp_err = NULL;
9677 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9678 g_print (_("Error: status-line: %s\n"), tmp_err->message);
9679 g_clear_error (&tmp_err);
9681 nmc->editor_status_line = bb;
9682 } else if (cmd_arg_p && matches (cmd_arg_p, "save-confirmation") == 0) {
9683 GError *tmp_err = NULL;
9685 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9686 g_print (_("Error: save-confirmation: %s\n"), tmp_err->message);
9687 g_clear_error (&tmp_err);
9689 nmc->editor_save_confirmation = bb;
9690 } else if (cmd_arg_p && matches (cmd_arg_p, "show-secrets") == 0) {
9691 GError *tmp_err = NULL;
9693 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9694 g_print (_("Error: show-secrets: %s\n"), tmp_err->message);
9695 g_clear_error (&tmp_err);
9697 nmc->editor_show_secrets = bb;
9698 } else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color") == 0) {
9699 GError *tmp_err = NULL;
9701 color = nmc_term_color_parse_string (cmd_arg_v ? g_strstrip (cmd_arg_v) : " ", &tmp_err);
9703 g_print (_("Error: bad color: %s\n"), tmp_err->message);
9704 g_clear_error (&tmp_err);
9706 nmc->editor_prompt_color = color;
9707 g_free (menu_ctx.main_prompt);
9708 if (menu_ctx.level == 0)
9709 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9712 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9714 nm_setting_get_name (menu_ctx.curr_setting));
9716 } else if (!cmd_arg_p) {
9717 g_print (_("Current nmcli configuration:\n"));
9718 g_print ("status-line: %s\n"
9719 "save-confirmation: %s\n"
9720 "show-secrets: %s\n"
9721 "prompt-color: %d\n",
9722 nmc->editor_status_line ? "yes" : "no",
9723 nmc->editor_save_confirmation ? "yes" : "no",
9724 nmc->editor_show_secrets ? "yes" : "no",
9725 nmc->editor_prompt_color);
9727 g_print (_("Invalid configuration option '%s'; allowed [%s]\n"),
9728 cmd_arg_v ? cmd_arg_v : "", "status-line, save-confirmation, show-secrets, prompt-color");
9732 case NMC_EDITOR_MAIN_CMD_QUIT:
9733 if (is_connection_dirty (connection, rem_con)) {
9734 if (confirm_quit ())
9735 cmd_loop = FALSE; /* quit command loop */
9737 cmd_loop = FALSE; /* quit command loop */
9740 case NMC_EDITOR_MAIN_CMD_UNKNOWN:
9742 g_print (_("Unknown command: '%s'\n"), cmd_user);
9752 g_free (valid_settings_str);
9753 g_free (menu_ctx.main_prompt);
9754 g_strfreev (menu_ctx.valid_props);
9755 g_free (menu_ctx.valid_props_str);
9757 g_object_unref (rem_con);
9758 g_weak_ref_clear (&weak);
9760 /* Save history file */
9761 save_history_cmds (nm_connection_get_uuid (connection));
9767 get_ethernet_device_name (NmCli *nmc)
9769 const GPtrArray *devices;
9772 devices = nm_client_get_devices (nmc->client);
9773 for (i = 0; i < devices->len; i++) {
9774 NMDevice *dev = g_ptr_array_index (devices, i);
9775 if (NM_IS_DEVICE_ETHERNET (dev))
9776 return nm_device_get_iface (dev);
9782 editor_init_new_connection (NmCli *nmc, NMConnection *connection)
9784 NMSetting *setting, *base_setting;
9785 NMSettingConnection *s_con;
9786 const char *con_type;
9787 const char *slave_type = NULL;
9789 s_con = nm_connection_get_setting_connection (connection);
9791 con_type = nm_setting_connection_get_connection_type (s_con);
9793 /* Initialize new connection according to its type using sensible defaults. */
9795 nmc_setting_connection_connect_handlers (s_con, connection);
9797 if (g_strcmp0 (con_type, "bond-slave") == 0)
9798 slave_type = NM_SETTING_BOND_SETTING_NAME;
9799 if (g_strcmp0 (con_type, "team-slave") == 0)
9800 slave_type = NM_SETTING_TEAM_SETTING_NAME;
9801 if (g_strcmp0 (con_type, "bridge-slave") == 0)
9802 slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
9805 const char *dev_ifname = get_ethernet_device_name (nmc);
9807 /* For bond/team/bridge slaves add 'wired' setting */
9808 setting = nm_setting_wired_new ();
9809 nm_connection_add_setting (connection, setting);
9811 g_object_set (s_con,
9812 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
9813 NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
9814 NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
9817 /* Add a "base" setting to the connection by default */
9818 base_setting = nmc_setting_new_for_name (con_type);
9821 nm_connection_add_setting (connection, base_setting);
9823 /* Set a sensible bond/team/bridge interface name by default */
9824 if (g_strcmp0 (con_type, NM_SETTING_BOND_SETTING_NAME) == 0)
9825 g_object_set (s_con,
9826 NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bond",
9828 if (g_strcmp0 (con_type, NM_SETTING_TEAM_SETTING_NAME) == 0)
9829 g_object_set (s_con,
9830 NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-team",
9832 if (g_strcmp0 (con_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0)
9833 g_object_set (s_con,
9834 NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bridge",
9837 /* Set sensible initial VLAN values */
9838 if (g_strcmp0 (con_type, NM_SETTING_VLAN_SETTING_NAME) == 0) {
9839 const char *dev_ifname = get_ethernet_device_name (nmc);
9841 g_object_set (NM_SETTING_VLAN (base_setting),
9842 NM_SETTING_VLAN_PARENT, dev_ifname ? dev_ifname : "eth0",
9843 NM_SETTING_VLAN_ID, 1,
9845 g_object_set (s_con,
9846 NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
9847 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_VLAN_SETTING_NAME,
9851 /* Initialize 'transport-mode' so that 'infiniband' is valid */
9852 if (g_strcmp0 (con_type, NM_SETTING_INFINIBAND_SETTING_NAME) == 0)
9853 g_object_set (NM_SETTING_INFINIBAND (base_setting),
9854 NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram",
9857 /* Initialize 'number' so that 'cdma' is valid */
9858 if (g_strcmp0 (con_type, NM_SETTING_CDMA_SETTING_NAME) == 0)
9859 g_object_set (NM_SETTING_CDMA (base_setting),
9860 NM_SETTING_CDMA_NUMBER, "#777",
9863 /* Initialize 'number' so that 'gsm' is valid */
9864 if (g_strcmp0 (con_type, NM_SETTING_GSM_SETTING_NAME) == 0)
9865 g_object_set (NM_SETTING_GSM (base_setting),
9866 NM_SETTING_GSM_NUMBER, "*99#",
9870 if (g_strcmp0 (con_type, NM_SETTING_WIRELESS_SETTING_NAME) == 0) {
9871 /* For Wi-Fi set mode to "infrastructure". Even though mode == NULL
9872 * is regarded as "infrastructure", explicit value makes no doubts.
9874 g_object_set (NM_SETTING_WIRELESS (base_setting),
9875 NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
9878 /* Do custom initialization for wifi setting */
9879 nmc_setting_custom_init (base_setting);
9883 if (g_strcmp0 (con_type, NM_SETTING_ADSL_SETTING_NAME) == 0) {
9884 /* Initialize a protocol */
9885 g_object_set (NM_SETTING_ADSL (base_setting),
9886 NM_SETTING_ADSL_PROTOCOL, NM_SETTING_ADSL_PROTOCOL_PPPOE,
9890 /* Always add IPv4 and IPv6 settings for non-slave connections */
9891 setting = nm_setting_ip4_config_new ();
9892 nmc_setting_custom_init (setting);
9893 nm_connection_add_setting (connection, setting);
9895 setting = nm_setting_ip6_config_new ();
9896 nmc_setting_custom_init (setting);
9897 nm_connection_add_setting (connection, setting);
9902 editor_init_existing_connection (NMConnection *connection)
9904 NMSettingIPConfig *s_ip4, *s_ip6;
9905 NMSettingWireless *s_wireless;
9906 NMSettingConnection *s_con;
9908 s_ip4 = nm_connection_get_setting_ip4_config (connection);
9909 s_ip6 = nm_connection_get_setting_ip6_config (connection);
9910 s_wireless = nm_connection_get_setting_wireless (connection);
9911 s_con = nm_connection_get_setting_connection (connection);
9914 nmc_setting_ip4_connect_handlers (s_ip4);
9916 nmc_setting_ip6_connect_handlers (s_ip6);
9918 nmc_setting_wireless_connect_handlers (s_wireless);
9920 nmc_setting_connection_connect_handlers (s_con, connection);
9923 static NMCResultCode
9924 do_connection_edit (NmCli *nmc, int argc, char **argv)
9926 NMConnection *connection = NULL;
9927 NMSettingConnection *s_con;
9928 const char *connection_type;
9930 char *default_name = NULL;
9931 const char *type = NULL;
9932 char *type_ask = NULL;
9933 const char *con_name = NULL;
9934 const char *con = NULL;
9935 const char *con_id = NULL;
9936 const char *con_uuid = NULL;
9937 const char *con_path = NULL;
9938 const char *selector = NULL;
9940 GError *error = NULL;
9941 GError *err1 = NULL;
9942 nmc_arg_t exp_args[] = { {"type", TRUE, &type, FALSE},
9943 {"con-name", TRUE, &con_name, FALSE},
9944 {"id", TRUE, &con_id, FALSE},
9945 {"uuid", TRUE, &con_uuid, FALSE},
9946 {"path", TRUE, &con_path, FALSE},
9949 nmc->return_value = NMC_RESULT_SUCCESS;
9954 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, &error)) {
9955 g_string_assign (nmc->return_text, error->message);
9956 nmc->return_value = error->code;
9957 g_clear_error (&error);
9962 /* Setup some readline completion stuff */
9963 /* Set a pointer to an alternative function to create matches */
9964 rl_attempted_completion_function = (rl_completion_func_t *) nmcli_editor_tab_completion;
9965 /* Use ' ' and '.' as word break characters */
9966 rl_completer_word_break_characters = ". ";
9969 if (con_id && !con_uuid && !con_path) {
9972 } else if (con_uuid && !con_id && !con_path) {
9975 } else if (con_path && !con_id && !con_uuid) {
9978 } else if (!con_path && !con_id && !con_uuid) {
9981 g_string_printf (nmc->return_text,
9982 _("Error: only one of 'id', uuid, or 'path' can be provided."));
9983 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
9989 /* Existing connection */
9990 NMConnection *found_con;
9992 found_con = nmc_find_connection (nmc->connections, selector, con, NULL);
9994 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con);
9995 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
9999 /* Duplicate the connection and use that so that we need not
10000 * differentiate existing vs. new later
10002 connection = nm_simple_connection_new_clone (found_con);
10004 /* Merge secrets into the connection */
10005 update_secrets_in_connection (NM_REMOTE_CONNECTION (found_con), connection);
10007 s_con = nm_connection_get_setting_connection (connection);
10009 connection_type = nm_setting_connection_get_connection_type (s_con);
10012 g_print (_("Warning: editing existing connection '%s'; 'type' argument is ignored\n"),
10013 nm_connection_get_id (connection));
10015 g_print (_("Warning: editing existing connection '%s'; 'con-name' argument is ignored\n"),
10016 nm_connection_get_id (connection));
10018 /* Load previously saved history commands for the connection */
10019 load_history_cmds (nm_connection_get_uuid (connection));
10021 editor_init_existing_connection (connection);
10023 /* New connection */
10024 connection_type = check_valid_name (type, nmc_valid_connection_types, NULL, &err1);
10025 tmp_str = get_valid_options_string (nmc_valid_connection_types, NULL);
10027 while (!connection_type) {
10029 g_print (_("Valid connection types: %s\n"), tmp_str);
10031 g_print (_("Error: invalid connection type; %s\n"), err1->message);
10032 g_clear_error (&err1);
10034 type_ask = nmc_readline (EDITOR_PROMPT_CON_TYPE);
10035 type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
10036 connection_type = check_valid_name (type_ask, nmc_valid_connection_types, NULL, &err1);
10041 /* Create a new connection object */
10042 connection = nm_simple_connection_new ();
10044 /* Build up the 'connection' setting */
10045 s_con = (NMSettingConnection *) nm_setting_connection_new ();
10046 uuid = nm_utils_uuid_generate ();
10048 default_name = g_strdup (con_name);
10050 default_name = nmc_unique_connection_name (nmc->connections,
10051 get_name_alias (connection_type, nmc_valid_connection_types));
10053 g_object_set (s_con,
10054 NM_SETTING_CONNECTION_ID, default_name,
10055 NM_SETTING_CONNECTION_UUID, uuid,
10056 NM_SETTING_CONNECTION_TYPE, connection_type,
10059 g_free (default_name);
10060 nm_connection_add_setting (connection, NM_SETTING (s_con));
10062 /* Initialize the new connection so that it is valid from the start */
10063 editor_init_new_connection (nmc, connection);
10066 /* nmcli runs the editor */
10067 nmc->in_editor = TRUE;
10070 g_print (_("===| nmcli interactive connection editor |==="));
10073 g_print (_("Editing existing '%s' connection: '%s'"), connection_type, con);
10075 g_print (_("Adding a new '%s' connection"), connection_type);
10077 g_print (_("Type 'help' or '?' for available commands."));
10079 g_print (_("Type 'describe [<setting>.<prop>]' for detailed property description."));
10082 /* Set global variables for use in TAB completion */
10083 nmc_tab_completion.nmc = nmc;
10084 nmc_tab_completion.con_type = g_strdup (connection_type);
10085 nmc_tab_completion.connection = connection;
10087 /* Run menu loop */
10088 editor_menu_main (nmc, connection, connection_type);
10091 g_object_unref (connection);
10092 g_free (nmc_tab_completion.con_type);
10094 nmc->should_wait++;
10095 return nmc->return_value;
10098 g_assert (!connection);
10101 nmc->should_wait++;
10102 return nmc->return_value;
10107 modify_connection_cb (GObject *connection,
10108 GAsyncResult *result,
10109 gpointer user_data)
10111 NmCli *nmc = (NmCli *) user_data;
10112 GError *error = NULL;
10114 if (!nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
10116 g_string_printf (nmc->return_text,
10117 _("Error: Failed to modify connection '%s': %s"),
10118 nm_connection_get_id (NM_CONNECTION (connection)),
10120 g_error_free (error);
10121 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10123 if (nmc->print_output == NMC_PRINT_PRETTY)
10124 g_print (_("Connection '%s' (%s) successfully modified.\n"),
10125 nm_connection_get_id (NM_CONNECTION (connection)),
10126 nm_connection_get_uuid (NM_CONNECTION (connection)));
10131 static NMCResultCode
10132 do_connection_modify (NmCli *nmc,
10133 gboolean temporary,
10137 NMConnection *connection = NULL;
10138 NMRemoteConnection *rc = NULL;
10140 const char *selector = NULL;
10141 GError *error = NULL;
10143 nmc->return_value = NMC_RESULT_SUCCESS;
10146 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10147 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10150 if ( strcmp (*argv, "id") == 0
10151 || strcmp (*argv, "uuid") == 0
10152 || strcmp (*argv, "path") == 0) {
10155 if (next_arg (&argc, &argv) != 0) {
10156 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10158 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10165 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10166 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10169 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10171 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10172 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10175 rc = nm_client_get_connection_by_uuid (nmc->client,
10176 nm_connection_get_uuid (connection));
10178 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10179 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10183 if (next_arg (&argc, &argv) != 0) {
10184 g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing."));
10185 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10189 if (!read_connection_properties (NM_CONNECTION (rc), argc, argv, &error)) {
10190 g_string_assign (nmc->return_text, error->message);
10191 nmc->return_value = error->code;
10192 g_clear_error (&error);
10196 update_connection (!temporary, rc, modify_connection_cb, nmc);
10198 nmc->should_wait++;
10200 return nmc->return_value;
10208 } CloneConnectionInfo;
10211 clone_connection_cb (GObject *client,
10212 GAsyncResult *result,
10213 gpointer user_data)
10215 CloneConnectionInfo *info = (CloneConnectionInfo *) user_data;
10216 NmCli *nmc = info->nmc;
10217 NMRemoteConnection *connection;
10218 GError *error = NULL;
10220 connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
10222 g_string_printf (nmc->return_text,
10223 _("Error: Failed to add '%s' connection: %s"),
10224 info->con_id, error->message);
10225 g_error_free (error);
10226 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
10228 g_print (_("%s (%s) cloned as %s (%s).\n"),
10231 nm_connection_get_id (NM_CONNECTION (connection)),
10232 nm_connection_get_uuid (NM_CONNECTION (connection)));
10233 g_object_unref (connection);
10236 g_free (info->con_id);
10237 g_free (info->orig_id);
10238 g_free (info->orig_uuid);
10239 g_slice_free (CloneConnectionInfo, info);
10243 static NMCResultCode
10244 do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv)
10246 NMConnection *connection = NULL;
10247 NMConnection *new_connection = NULL;
10248 NMSettingConnection *s_con;
10249 CloneConnectionInfo *info;
10251 const char *new_name;
10252 char *name_ask = NULL;
10253 char *new_name_ask = NULL;
10254 const char *selector = NULL;
10259 name = name_ask = nmc_readline (PROMPT_CONNECTION);
10260 new_name = new_name_ask = nmc_readline (_("New connection name: "));
10262 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10263 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10267 if ( strcmp (*argv, "id") == 0
10268 || strcmp (*argv, "uuid") == 0
10269 || strcmp (*argv, "path") == 0) {
10272 if (next_arg (&argc, &argv) != 0) {
10273 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10275 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10280 if (next_arg (&argc, &argv) != 0) {
10281 g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
10282 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10286 if (next_arg (&argc, &argv) == 0) {
10287 g_string_printf (nmc->return_text, _("Error: unexpected extra argument '%s'."), *argv);
10288 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10294 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10295 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10299 g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
10300 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10304 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10306 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10307 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10311 /* Copy the connection */
10312 new_connection = nm_simple_connection_new_clone (connection);
10314 s_con = nm_connection_get_setting_connection (new_connection);
10316 uuid = nm_utils_uuid_generate ();
10317 g_object_set (s_con,
10318 NM_SETTING_CONNECTION_ID, new_name,
10319 NM_SETTING_CONNECTION_UUID, uuid,
10323 /* Merge secrets into the new connection */
10324 update_secrets_in_connection (NM_REMOTE_CONNECTION (connection), new_connection);
10326 info = g_slice_new0 (CloneConnectionInfo);
10328 info->orig_id = g_strdup (nm_connection_get_id (connection));
10329 info->orig_uuid = g_strdup (nm_connection_get_uuid (connection));
10330 info->con_id = g_strdup (nm_connection_get_id (new_connection));
10332 /* Add the new cloned connection to NetworkManager */
10333 add_new_connection (!temporary,
10336 clone_connection_cb,
10339 nmc->should_wait = TRUE;
10341 if (new_connection)
10342 g_object_unref (new_connection);
10344 g_free (new_name_ask);
10346 return nmc->return_value;
10350 delete_cb (GObject *con, GAsyncResult *result, gpointer user_data)
10352 ConnectionCbInfo *info = (ConnectionCbInfo *) user_data;
10353 GError *error = NULL;
10355 if (!nm_remote_connection_delete_finish (NM_REMOTE_CONNECTION (con), result, &error)) {
10356 g_string_printf (info->nmc->return_text, _("Error: not all connections deleted."));
10357 g_printerr (_("Error: Connection deletion failed: %s"),
10359 g_error_free (error);
10360 info->nmc->return_value = NMC_RESULT_ERROR_CON_DEL;
10361 connection_cb_info_finish (info, con);
10363 if (info->nmc->nowait_flag)
10364 connection_cb_info_finish (info, con);
10368 static NMCResultCode
10369 do_connection_delete (NmCli *nmc, int argc, char **argv)
10371 NMConnection *connection;
10372 ConnectionCbInfo *info = NULL;
10373 GSList *queue = NULL, *iter;
10374 char **arg_arr = NULL;
10375 char **arg_ptr = argv;
10376 int arg_num = argc;
10377 GString *invalid_cons = NULL;
10380 if (nmc->timeout == -1)
10385 char *line = nmc_readline (PROMPT_CONNECTIONS);
10386 nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
10390 if (arg_num == 0) {
10391 g_string_printf (nmc->return_text, _("Error: No connection specified."));
10392 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10397 while (arg_num > 0) {
10398 const char *selector = NULL;
10400 if ( strcmp (*arg_ptr, "id") == 0
10401 || strcmp (*arg_ptr, "uuid") == 0
10402 || strcmp (*arg_ptr, "path") == 0) {
10403 selector = *arg_ptr;
10404 if (next_arg (&arg_num, &arg_ptr) != 0) {
10405 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
10406 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10411 connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos);
10413 /* Check if the connection is unique. */
10414 /* Calling delete for the same connection repeatedly would result in
10415 * NM responding for the last D-Bus call only and we would stall. */
10416 if (!g_slist_find (queue, connection))
10417 queue = g_slist_prepend (queue, g_object_ref (connection));
10419 g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr);
10420 g_string_printf (nmc->return_text, _("Error: not all active connections found."));
10421 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10423 invalid_cons = g_string_new (NULL);
10424 g_string_append_printf (invalid_cons, "'%s', ", *arg_ptr);
10427 /* Take next argument (if there's no other connection of the same name) */
10429 next_arg (&arg_num, &arg_ptr);
10433 g_string_printf (nmc->return_text, _("Error: no connection provided."));
10434 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10437 queue = g_slist_reverse (queue);
10439 info = g_slice_new0 (ConnectionCbInfo);
10441 info->queue = queue;
10442 info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
10444 nmc->nowait_flag = (nmc->timeout == 0);
10445 nmc->should_wait++;
10447 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED,
10448 G_CALLBACK (connection_removed_cb), info);
10450 /* Now delete the connections */
10451 for (iter = queue; iter; iter = g_slist_next (iter))
10452 nm_remote_connection_delete_async (NM_REMOTE_CONNECTION (iter->data),
10453 NULL, delete_cb, info);
10456 if (invalid_cons) {
10457 g_string_truncate (invalid_cons, invalid_cons->len-2); /* truncate trailing ", " */
10458 g_string_printf (nmc->return_text, _("Error: cannot delete unknown connection(s): %s."),
10459 invalid_cons->str);
10460 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10461 g_string_free (invalid_cons, TRUE);
10463 g_strfreev (arg_arr);
10464 return nmc->return_value;
10468 connection_changed (NMConnection *connection, NmCli *nmc)
10470 g_print (_("%s: connection profile changed\n"), nm_connection_get_id (connection));
10474 connection_watch (NmCli *nmc, NMConnection *connection)
10476 nmc->should_wait++;
10477 g_signal_connect (connection, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed), nmc);
10481 connection_unwatch (NmCli *nmc, NMConnection *connection)
10483 if (g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_changed), nmc))
10484 nmc->should_wait--;
10486 /* Terminate if all the watched connections disappeared. */
10487 if (!nmc->should_wait)
10492 connection_added (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
10494 NMConnection *connection = NM_CONNECTION (con);
10496 g_print (_("%s: connection profile created\n"), nm_connection_get_id (connection));
10497 connection_watch (nmc, connection);
10501 connection_removed (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
10503 NMConnection *connection = NM_CONNECTION (con);
10505 g_print (_("%s: connection profile removed\n"), nm_connection_get_id (connection));
10506 connection_unwatch (nmc, connection);
10509 static NMCResultCode
10510 do_connection_monitor (NmCli *nmc, int argc, char **argv)
10513 /* No connections specified. Monitor all. */
10516 nmc->connections = nm_client_get_connections (nmc->client);
10517 for (i = 0; i < nmc->connections->len; i++)
10518 connection_watch (nmc, g_ptr_array_index (nmc->connections, i));
10520 /* We'll watch the connection additions too, never exit. */
10521 nmc->should_wait++;
10522 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_ADDED, G_CALLBACK (connection_added), nmc);
10524 /* Look up the specified connections and watch them. */
10525 NMConnection *connection;
10526 char **arg_ptr = argv;
10527 int arg_num = argc;
10531 const char *selector = NULL;
10533 if ( strcmp (*arg_ptr, "id") == 0
10534 || strcmp (*arg_ptr, "uuid") == 0
10535 || strcmp (*arg_ptr, "path") == 0) {
10536 selector = *arg_ptr;
10537 if (next_arg (&arg_num, &arg_ptr) != 0) {
10538 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
10539 return NMC_RESULT_ERROR_USER_INPUT;
10543 connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos);
10545 connection_watch (nmc, connection);
10547 g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr);
10548 g_string_printf (nmc->return_text, _("Error: not all connections found."));
10549 return NMC_RESULT_ERROR_NOT_FOUND;
10552 /* Take next argument (if there's no other connection of the same name) */
10554 next_arg (&arg_num, &arg_ptr);
10555 } while (arg_num > 0);
10558 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED, G_CALLBACK (connection_removed), nmc);
10560 return NMC_RESULT_SUCCESS;
10563 static NMCResultCode
10564 do_connection_reload (NmCli *nmc, int argc, char **argv)
10566 GError *error = NULL;
10568 nmc->return_value = NMC_RESULT_SUCCESS;
10570 if (!nm_client_reload_connections (nmc->client, NULL, &error)) {
10571 g_string_printf (nmc->return_text, _("Error: failed to reload connections: %s."),
10573 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10574 g_clear_error (&error);
10577 return nmc->return_value;
10580 static NMCResultCode
10581 do_connection_load (NmCli *nmc, int argc, char **argv)
10583 GError *error = NULL;
10584 char **filenames, **failures = NULL;
10587 nmc->return_value = NMC_RESULT_SUCCESS;
10590 g_string_printf (nmc->return_text, _("Error: No connection specified."));
10591 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10592 return nmc->return_value;
10595 filenames = g_new (char *, argc + 1);
10596 for (i = 0; i < argc; i++)
10597 filenames[i] = argv[i];
10598 filenames[i] = NULL;
10600 nm_client_load_connections (nmc->client, filenames, &failures, NULL, &error);
10601 g_free (filenames);
10603 g_string_printf (nmc->return_text, _("Error: failed to load connection: %s."),
10605 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10606 g_error_free (error);
10610 for (i = 0; failures[i]; i++)
10611 g_printerr (_("Could not load file '%s'\n"), failures[i]);
10612 g_strfreev (failures);
10615 return nmc->return_value;
10618 // FIXME: change the text when non-VPN connection types are supported
10619 #define PROMPT_IMPORT_TYPE PROMPT_VPN_TYPE
10620 #define PROMPT_IMPORT_FILE _("File to import: ")
10622 static NMCResultCode
10623 do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv)
10625 GError *error = NULL;
10626 const char *type = NULL, *filename = NULL;
10627 char *type_ask = NULL, *filename_ask = NULL;
10628 AddConnectionInfo *info;
10629 NMConnection *connection = NULL;
10630 NMVpnEditorPlugin *plugin;
10634 type_ask = nmc_readline (PROMPT_IMPORT_TYPE);
10635 filename_ask = nmc_readline (PROMPT_IMPORT_FILE);
10636 type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
10637 filename = filename_ask = filename_ask ? g_strstrip (filename_ask) : NULL;
10639 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10640 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10646 if (strcmp (*argv, "type") == 0) {
10647 if (next_arg (&argc, &argv) != 0) {
10648 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
10649 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10655 g_printerr (_("Warning: 'type' already specified, ignoring extra one.\n"));
10657 } else if (strcmp (*argv, "file") == 0) {
10658 if (next_arg (&argc, &argv) != 0) {
10659 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
10660 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10666 g_printerr (_("Warning: 'file' already specified, ignoring extra one.\n"));
10668 g_string_printf (nmc->return_text, _("Unknown parameter: %s"), *argv);
10669 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10678 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
10679 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10683 g_string_printf (nmc->return_text, _("Error: 'file' argument is required."));
10684 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10688 /* Import VPN configuration */
10689 plugin = nm_vpn_get_plugin_by_service (type, &error);
10691 g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
10693 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10697 connection = nm_vpn_editor_plugin_import (plugin, filename, &error);
10699 g_string_printf (nmc->return_text, _("Error: failed to import '%s': %s."),
10700 filename, error->message);
10701 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10705 info = g_malloc0 (sizeof (AddConnectionInfo));
10707 info->con_name = g_strdup (nm_connection_get_id (connection));
10709 /* Add the new imported connection to NetworkManager */
10710 add_new_connection (!temporary,
10716 nmc->should_wait = TRUE;
10719 g_object_unref (connection);
10720 g_clear_error (&error);
10722 g_free (filename_ask);
10723 return nmc->return_value;
10726 static NMCResultCode
10727 do_connection_export (NmCli *nmc, int argc, char **argv)
10729 NMConnection *connection = NULL;
10731 const char *out_name = NULL;
10732 char *name_ask = NULL;
10733 char *out_name_ask = NULL;
10734 const char *path = NULL;
10735 const char *selector = NULL;
10736 const char *type = NULL;
10737 NMVpnEditorPlugin *plugin;
10738 GError *error = NULL;
10739 char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX";
10743 name_ask = nmc_readline (PROMPT_VPN_CONNECTION);
10744 name = name_ask = name_ask ? g_strstrip (name_ask) : NULL;
10745 out_name = out_name_ask = nmc_readline (_("Output file name: "));
10747 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10748 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10752 if ( strcmp (*argv, "id") == 0
10753 || strcmp (*argv, "uuid") == 0
10754 || strcmp (*argv, "path") == 0) {
10757 if (next_arg (&argc, &argv) != 0) {
10758 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10760 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10765 if (next_arg (&argc, &argv) == 0)
10768 if (next_arg (&argc, &argv) == 0) {
10769 g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv);
10770 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10776 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10777 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10780 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10782 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10783 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10787 type = nm_connection_get_connection_type (connection);
10788 if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) != 0) {
10789 g_string_printf (nmc->return_text, _("Error: the connection is not VPN."));
10790 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10793 type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
10795 /* Export VPN configuration */
10796 plugin = nm_vpn_get_plugin_by_service (type, &error);
10798 g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
10800 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10808 fd = g_mkstemp (tmpfile);
10810 g_string_printf (nmc->return_text, _("Error: failed to create temporary file %s."), tmpfile);
10811 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10818 if (!nm_vpn_editor_plugin_export (plugin, path, connection, &error)) {
10819 g_string_printf (nmc->return_text, _("Error: failed to export '%s': %s."),
10820 nm_connection_get_id (connection), error->message);
10821 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10825 /* No output file -> copy data to stdout */
10827 char *contents = NULL;
10829 if (!g_file_get_contents (path, &contents, &len, &error)) {
10830 g_string_printf (nmc->return_text, _("Error: failed to read temporary file '%s': %s."),
10831 path, error->message);
10832 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10835 g_print ("%s", contents);
10840 if (!out_name && path)
10842 g_clear_error (&error);
10844 g_free (out_name_ask);
10845 return nmc->return_value;
10853 } NmcEditorThreadData;
10855 static GThread *editor_thread;
10856 static NmcEditorThreadData editor_thread_data;
10859 * We need to run do_connection_edit() in a thread so that
10860 * glib main loop is not blocked and could receive and process D-Bus
10864 connection_editor_thread_func (gpointer data)
10866 NmcEditorThreadData *td = (NmcEditorThreadData *) data;
10868 /* run editor for editing/adding connections */
10869 td->nmc->return_value = do_connection_edit (td->nmc, td->argc, td->argv);
10871 /* quit glib main loop now that we are done with this thread */
10878 gen_func_connection_names (const char *text, int state)
10881 const char **connections;
10884 if (nm_cli.connections->len == 0)
10887 connections = g_new (const char *, nm_cli.connections->len + 1);
10888 for (i = 0; i < nm_cli.connections->len; i++) {
10889 NMConnection *con = NM_CONNECTION (nm_cli.connections->pdata[i]);
10890 const char *id = nm_connection_get_id (con);
10891 connections[i] = id;
10893 connections[i] = NULL;
10895 ret = nmc_rl_gen_func_basic (text, state, connections);
10897 g_free (connections);
10902 gen_func_active_connection_names (const char *text, int state)
10905 const GPtrArray *acs;
10906 const char **connections;
10909 if (!nm_cli.client)
10912 acs = nm_client_get_active_connections (nm_cli.client);
10913 if (!acs || acs->len == 0)
10916 connections = g_new (const char *, acs->len + 1);
10917 for (i = 0; i < acs->len; i++)
10918 connections[i] = nm_active_connection_get_id (acs->pdata[i]);
10919 connections[i] = NULL;
10921 ret = nmc_rl_gen_func_basic (text, state, connections);
10923 g_free (connections);
10928 nmcli_con_tab_completion (const char *text, int start, int end)
10930 char **match_array = NULL;
10931 rl_compentry_func_t *generator_func = NULL;
10933 /* Disable readline's default filename completion */
10934 rl_attempted_completion_over = 1;
10936 if (g_strcmp0 (rl_prompt, PROMPT_CONNECTION) == 0) {
10937 /* Disable appending space after completion */
10938 rl_completion_append_character = '\0';
10940 if (!is_single_word (rl_line_buffer))
10943 generator_func = gen_func_connection_names;
10944 } else if (g_strcmp0 (rl_prompt, PROMPT_CONNECTIONS) == 0) {
10945 generator_func = gen_func_connection_names;
10946 } else if (g_strcmp0 (rl_prompt, PROMPT_ACTIVE_CONNECTIONS) == 0) {
10947 generator_func = gen_func_active_connection_names;
10948 } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_TYPE) == 0) {
10949 generator_func = gen_func_vpn_types;
10950 } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_FILE) == 0) {
10951 rl_attempted_completion_over = 0;
10952 rl_complete_with_tilde_expansion = 1;
10953 } else if (g_strcmp0 (rl_prompt, PROMPT_VPN_CONNECTION) == 0) {
10954 generator_func = gen_vpn_ids;
10957 if (generator_func)
10958 match_array = rl_completion_matches (text, generator_func);
10960 return match_array;
10964 parse_preferred_connection_order (const char *order, GError **error)
10966 char **strv, **iter;
10970 gboolean inverse, unique;
10973 strv = nmc_strsplit_set (order, ":", -1);
10974 if (!strv || !*strv) {
10975 g_set_error (error, NMCLI_ERROR, 0,
10976 _("incorrect string '%s' of '--order' option"), order);
10981 order_arr = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), 4);
10982 for (iter = strv; iter && *iter; iter++) {
10987 if (str[0] == '+' || str[0] == '-')
10990 if (matches (str, "active") == 0)
10991 val = inverse ? NMC_SORT_ACTIVE_INV : NMC_SORT_ACTIVE;
10992 else if (matches (str, "name") == 0)
10993 val = inverse ? NMC_SORT_NAME_INV : NMC_SORT_NAME;
10994 else if (matches (str, "type") == 0)
10995 val = inverse ? NMC_SORT_TYPE_INV : NMC_SORT_TYPE;
10996 else if (matches (str, "path") == 0)
10997 val = inverse ? NMC_SORT_PATH_INV : NMC_SORT_PATH;
10999 g_array_unref (order_arr);
11001 g_set_error (error, NMCLI_ERROR, 0,
11002 _("incorrect item '%s' in '--order' option"), *iter);
11005 /* Check for duplicates and ignore them. */
11007 for (i = 0; i < order_arr->len; i++) {
11008 if (abs (g_array_index (order_arr, NmcSortOrder, i)) - abs (val) == 0) {
11014 /* Value is ok and unique, add it to the array */
11016 g_array_append_val (order_arr, val);
11023 /* Entry point function for connections-related commands: 'nmcli connection' */
11025 do_connections (NmCli *nmc, int argc, char **argv)
11027 GError *error = NULL;
11029 /* Register polkit agent */
11030 nmc_start_polkit_agent_start_try (nmc);
11032 /* Set completion function for 'nmcli con' */
11033 rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_tab_completion;
11035 /* Exit early on help */
11036 if (nmc_arg_is_help (*argv)) {
11038 return nmc->return_value;
11040 if (argc != 0 && nmc_arg_is_help (*(argv+1))) {
11041 if (usage_connection_second_level (*argv))
11042 return nmc->return_value;
11045 /* Get NMClient object early */
11046 nmc->get_client (nmc);
11048 /* Check whether NetworkManager is running */
11049 if (!nm_client_get_nm_running (nmc->client)) {
11050 g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
11051 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
11052 return nmc->return_value;
11054 /* Compare NM and nmcli versions */
11055 if (!nmc_versions_match (nmc))
11056 return nmc->return_value;
11058 /* Get the connection list */
11059 nmc->connections = nm_client_get_connections (nmc->client);
11061 /* Now parse the command line and perform the required operation */
11063 if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
11065 nmc->return_value = do_connections_show (nmc, FALSE, FALSE, NULL, argc, argv);
11067 if (matches (*argv, "show") == 0) {
11068 gboolean active = FALSE;
11069 gboolean show_secrets = FALSE;
11070 GArray *order = NULL;
11073 next_arg (&argc, &argv);
11074 /* check connection show options [--active] [--show-secrets] */
11075 for (i = 0; i < 3; i++) {
11076 if (!active && nmc_arg_is_option (*argv, "active")) {
11078 next_arg (&argc, &argv);
11080 /* --show-secrets is deprecated in favour of global --show-secrets */
11081 /* Keep it here for backwards compatibility */
11082 if (!show_secrets && nmc_arg_is_option (*argv, "show-secrets")) {
11083 show_secrets = TRUE;
11084 next_arg (&argc, &argv);
11086 if (!order && nmc_arg_is_option (*argv, "order")) {
11087 if (next_arg (&argc, &argv) != 0) {
11088 g_set_error_literal (&error, NMCLI_ERROR, 0,
11089 _("'--order' argument is missing"));
11092 order = parse_preferred_connection_order (*argv, &error);
11095 next_arg (&argc, &argv);
11098 show_secrets = nmc->show_secrets || show_secrets;
11099 nmc->return_value = do_connections_show (nmc, active, show_secrets, order, argc, argv);
11101 g_array_unref (order);
11102 } else if (matches(*argv, "up") == 0) {
11103 nmc->return_value = do_connection_up (nmc, argc-1, argv+1);
11104 } else if (matches(*argv, "down") == 0) {
11105 nmc->return_value = do_connection_down (nmc, argc-1, argv+1);
11106 } else if (matches(*argv, "add") == 0) {
11107 nmc->return_value = do_connection_add (nmc, argc-1, argv+1);
11108 } else if (matches(*argv, "edit") == 0) {
11109 nmc->should_wait++;
11110 editor_thread_data.nmc = nmc;
11111 editor_thread_data.argc = argc - 1;
11112 editor_thread_data.argv = argv + 1;
11113 editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data);
11114 g_thread_unref (editor_thread);
11115 } else if (matches(*argv, "delete") == 0) {
11116 nmc->return_value = do_connection_delete (nmc, argc-1, argv+1);
11117 } else if (matches(*argv, "reload") == 0) {
11118 nmc->return_value = do_connection_reload (nmc, argc-1, argv+1);
11119 } else if (matches(*argv, "load") == 0) {
11120 nmc->return_value = do_connection_load (nmc, argc-1, argv+1);
11121 } else if (matches (*argv, "modify") == 0) {
11122 gboolean temporary = FALSE;
11124 next_arg (&argc, &argv);
11125 if (nmc_arg_is_option (*argv, "temporary")) {
11127 next_arg (&argc, &argv);
11129 nmc->return_value = do_connection_modify (nmc, temporary, argc, argv);
11130 } else if (matches (*argv, "clone") == 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_clone (nmc, temporary, argc, argv);
11139 } else if (matches(*argv, "import") == 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_import (nmc, temporary, argc, argv);
11148 } else if (matches(*argv, "export") == 0) {
11149 nmc->return_value = do_connection_export (nmc, argc-1, argv+1);
11150 } else if (matches(*argv, "monitor") == 0) {
11151 nmc->return_value = do_connection_monitor (nmc, argc-1, argv+1);
11154 g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv);
11155 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
11159 return nmc->return_value;
11162 g_string_printf (nmc->return_text, _("Error: %s."), error->message);
11163 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
11164 g_error_free (error);
11165 return nmc->return_value;
11169 monitor_connections (NmCli *nmc)
11171 do_connection_monitor (nmc, 0, NULL);