1 /* nmcli - command-line tool to control NetworkManager
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 * Copyright 2010 - 2015 Red Hat, Inc.
20 #include "nm-default.h"
28 #include <netinet/ether.h>
29 #include <readline/readline.h>
30 #include <readline/history.h>
35 #include "connections.h"
36 #include "nm-secret-agent-simple.h"
37 #include "polkit-agent.h"
38 #include "nm-vpn-helpers.h"
40 /* define some prompts for connection editor */
41 #define EDITOR_PROMPT_SETTING _("Setting name? ")
42 #define EDITOR_PROMPT_PROPERTY _("Property name? ")
43 #define EDITOR_PROMPT_CON_TYPE _("Enter connection type: ")
45 /* define some other prompts */
46 #define PROMPT_CON_TYPE _("Connection type: ")
47 #define PROMPT_VPN_TYPE _("VPN type: ")
48 #define PROMPT_MASTER _("Master: ")
49 #define PROMPT_CONNECTION _("Connection (name, UUID, or path): ")
50 #define PROMPT_VPN_CONNECTION _("VPN connection (name, UUID, or path): ")
51 #define PROMPT_CONNECTIONS _("Connection(s) (name, UUID, or path): ")
52 #define PROMPT_ACTIVE_CONNECTIONS _("Connection(s) (name, UUID, path or apath): ")
53 #define PROMPT_IP_TUNNEL_MODE _("Tunnel mode: ")
54 #define PROMPT_MACVLAN_MODE _("MACVLAN mode: ")
56 static const char *nmc_known_vpns[] = {
71 /* Available fields for 'connection show' */
72 static NmcOutputField nmc_fields_con_show[] = {
73 {"NAME", N_("NAME")}, /* 0 */
74 {"UUID", N_("UUID")}, /* 1 */
75 {"TYPE", N_("TYPE")}, /* 2 */
76 {"TIMESTAMP", N_("TIMESTAMP")}, /* 3 */
77 {"TIMESTAMP-REAL", N_("TIMESTAMP-REAL")}, /* 4 */
78 {"AUTOCONNECT", N_("AUTOCONNECT")}, /* 5 */
79 {"AUTOCONNECT-PRIORITY", N_("AUTOCONNECT-PRIORITY")}, /* 6 */
80 {"READONLY", N_("READONLY")}, /* 7 */
81 {"DBUS-PATH", N_("DBUS-PATH")}, /* 8 */
82 {"ACTIVE", N_("ACTIVE")}, /* 9 */
83 {"DEVICE", N_("DEVICE")}, /* 10 */
84 {"STATE", N_("STATE")}, /* 11 */
85 {"ACTIVE-PATH", N_("ACTIVE-PATH")}, /* 12 */
88 #define NMC_FIELDS_CON_SHOW_ALL "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,AUTOCONNECT-PRIORITY,READONLY,DBUS-PATH,"\
89 "ACTIVE,DEVICE,STATE,ACTIVE-PATH"
90 #define NMC_FIELDS_CON_SHOW_COMMON "NAME,UUID,TYPE,DEVICE"
92 /* Helper macro to define fields */
93 #define SETTING_FIELD(setting, props) { setting, N_(setting), 0, props, NULL, FALSE, FALSE, 0 }
95 /* defined in settings.c */
96 extern NmcOutputField nmc_fields_setting_connection[];
97 extern NmcOutputField nmc_fields_setting_wired[];
98 extern NmcOutputField nmc_fields_setting_8021X[];
99 extern NmcOutputField nmc_fields_setting_wireless[];
100 extern NmcOutputField nmc_fields_setting_wireless_security[];
101 extern NmcOutputField nmc_fields_setting_ip4_config[];
102 extern NmcOutputField nmc_fields_setting_ip6_config[];
103 extern NmcOutputField nmc_fields_setting_serial[];
104 extern NmcOutputField nmc_fields_setting_ppp[];
105 extern NmcOutputField nmc_fields_setting_pppoe[];
106 extern NmcOutputField nmc_fields_setting_adsl[];
107 extern NmcOutputField nmc_fields_setting_gsm[];
108 extern NmcOutputField nmc_fields_setting_cdma[];
109 extern NmcOutputField nmc_fields_setting_bluetooth[];
110 extern NmcOutputField nmc_fields_setting_olpc_mesh[];
111 extern NmcOutputField nmc_fields_setting_vpn[];
112 extern NmcOutputField nmc_fields_setting_wimax[];
113 extern NmcOutputField nmc_fields_setting_infiniband[];
114 extern NmcOutputField nmc_fields_setting_bond[];
115 extern NmcOutputField nmc_fields_setting_vlan[];
116 extern NmcOutputField nmc_fields_setting_bridge[];
117 extern NmcOutputField nmc_fields_setting_bridge_port[];
118 extern NmcOutputField nmc_fields_setting_team[];
119 extern NmcOutputField nmc_fields_setting_team_port[];
120 extern NmcOutputField nmc_fields_setting_dcb[];
121 extern NmcOutputField nmc_fields_setting_tun[];
122 extern NmcOutputField nmc_fields_setting_ip_tunnel[];
123 extern NmcOutputField nmc_fields_setting_macvlan[];
124 extern NmcOutputField nmc_fields_setting_vxlan[];
126 /* Available settings for 'connection show <con>' - profile part */
127 static NmcOutputField nmc_fields_settings_names[] = {
128 SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME, nmc_fields_setting_connection + 1), /* 0 */
129 SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME, nmc_fields_setting_wired + 1), /* 1 */
130 SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME, nmc_fields_setting_8021X + 1), /* 2 */
131 SETTING_FIELD (NM_SETTING_WIRELESS_SETTING_NAME, nmc_fields_setting_wireless + 1), /* 3 */
132 SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, nmc_fields_setting_wireless_security + 1), /* 4 */
133 SETTING_FIELD (NM_SETTING_IP4_CONFIG_SETTING_NAME, nmc_fields_setting_ip4_config + 1), /* 5 */
134 SETTING_FIELD (NM_SETTING_IP6_CONFIG_SETTING_NAME, nmc_fields_setting_ip6_config + 1), /* 6 */
135 SETTING_FIELD (NM_SETTING_SERIAL_SETTING_NAME, nmc_fields_setting_serial + 1), /* 7 */
136 SETTING_FIELD (NM_SETTING_PPP_SETTING_NAME, nmc_fields_setting_ppp + 1), /* 8 */
137 SETTING_FIELD (NM_SETTING_PPPOE_SETTING_NAME, nmc_fields_setting_pppoe + 1), /* 9 */
138 SETTING_FIELD (NM_SETTING_GSM_SETTING_NAME, nmc_fields_setting_gsm + 1), /* 10 */
139 SETTING_FIELD (NM_SETTING_CDMA_SETTING_NAME, nmc_fields_setting_cdma + 1), /* 11 */
140 SETTING_FIELD (NM_SETTING_BLUETOOTH_SETTING_NAME, nmc_fields_setting_bluetooth + 1), /* 12 */
141 SETTING_FIELD (NM_SETTING_OLPC_MESH_SETTING_NAME, nmc_fields_setting_olpc_mesh + 1), /* 13 */
142 SETTING_FIELD (NM_SETTING_VPN_SETTING_NAME, nmc_fields_setting_vpn + 1), /* 14 */
143 SETTING_FIELD (NM_SETTING_WIMAX_SETTING_NAME, nmc_fields_setting_wimax + 1), /* 15 */
144 SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME, nmc_fields_setting_infiniband + 1), /* 16 */
145 SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME, nmc_fields_setting_bond + 1), /* 17 */
146 SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME, nmc_fields_setting_vlan + 1), /* 18 */
147 SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME, nmc_fields_setting_adsl + 1), /* 19 */
148 SETTING_FIELD (NM_SETTING_BRIDGE_SETTING_NAME, nmc_fields_setting_bridge + 1), /* 20 */
149 SETTING_FIELD (NM_SETTING_BRIDGE_PORT_SETTING_NAME, nmc_fields_setting_bridge_port + 1), /* 21 */
150 SETTING_FIELD (NM_SETTING_TEAM_SETTING_NAME, nmc_fields_setting_team + 1), /* 22 */
151 SETTING_FIELD (NM_SETTING_TEAM_PORT_SETTING_NAME, nmc_fields_setting_team_port + 1), /* 23 */
152 SETTING_FIELD (NM_SETTING_DCB_SETTING_NAME, nmc_fields_setting_dcb + 1), /* 24 */
153 SETTING_FIELD (NM_SETTING_TUN_SETTING_NAME, nmc_fields_setting_tun + 1), /* 25 */
154 SETTING_FIELD (NM_SETTING_IP_TUNNEL_SETTING_NAME, nmc_fields_setting_ip_tunnel + 1), /* 26 */
155 SETTING_FIELD (NM_SETTING_MACVLAN_SETTING_NAME, nmc_fields_setting_macvlan + 1), /* 27 */
156 SETTING_FIELD (NM_SETTING_VXLAN_SETTING_NAME, nmc_fields_setting_vxlan + 1), /* 28 */
157 {NULL, NULL, 0, NULL, NULL, FALSE, FALSE, 0}
159 #define NMC_FIELDS_SETTINGS_NAMES_ALL_X NM_SETTING_CONNECTION_SETTING_NAME","\
160 NM_SETTING_WIRED_SETTING_NAME","\
161 NM_SETTING_802_1X_SETTING_NAME","\
162 NM_SETTING_WIRELESS_SETTING_NAME","\
163 NM_SETTING_WIRELESS_SECURITY_SETTING_NAME","\
164 NM_SETTING_IP4_CONFIG_SETTING_NAME","\
165 NM_SETTING_IP6_CONFIG_SETTING_NAME","\
166 NM_SETTING_SERIAL_SETTING_NAME","\
167 NM_SETTING_PPP_SETTING_NAME","\
168 NM_SETTING_PPPOE_SETTING_NAME","\
169 NM_SETTING_ADSL_SETTING_NAME","\
170 NM_SETTING_GSM_SETTING_NAME","\
171 NM_SETTING_CDMA_SETTING_NAME","\
172 NM_SETTING_BLUETOOTH_SETTING_NAME","\
173 NM_SETTING_OLPC_MESH_SETTING_NAME","\
174 NM_SETTING_VPN_SETTING_NAME","\
175 NM_SETTING_INFINIBAND_SETTING_NAME","\
176 NM_SETTING_BOND_SETTING_NAME","\
177 NM_SETTING_VLAN_SETTING_NAME","\
178 NM_SETTING_BRIDGE_SETTING_NAME","\
179 NM_SETTING_BRIDGE_PORT_SETTING_NAME","\
180 NM_SETTING_TEAM_SETTING_NAME","\
181 NM_SETTING_TEAM_PORT_SETTING_NAME"," \
182 NM_SETTING_DCB_SETTING_NAME"," \
183 NM_SETTING_TUN_SETTING_NAME"," \
184 NM_SETTING_IP_TUNNEL_SETTING_NAME"," \
185 NM_SETTING_MACVLAN_SETTING_NAME"," \
186 NM_SETTING_VXLAN_SETTING_NAME
187 #define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X
189 /* Active connection data */
190 /* Available fields for GENERAL group */
191 static NmcOutputField nmc_fields_con_active_details_general[] = {
192 {"GROUP", N_("GROUP")}, /* 0 */
193 {"NAME", N_("NAME")}, /* 1 */
194 {"UUID", N_("UUID")}, /* 2 */
195 {"DEVICES", N_("DEVICES")}, /* 3 */
196 {"STATE", N_("STATE")}, /* 4 */
197 {"DEFAULT", N_("DEFAULT")}, /* 5 */
198 {"DEFAULT6", N_("DEFAULT6")}, /* 6 */
199 {"SPEC-OBJECT", N_("SPEC-OBJECT")}, /* 7 */
200 {"VPN", N_("VPN")}, /* 8 */
201 {"DBUS-PATH", N_("DBUS-PATH")}, /* 9 */
202 {"CON-PATH", N_("CON-PATH")}, /* 10 */
203 {"ZONE", N_("ZONE")}, /* 11 */
204 {"MASTER-PATH", N_("MASTER-PATH")}, /* 12 */
207 #define NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL "GROUP,NAME,UUID,DEVICES,STATE,DEFAULT,DEFAULT6,"\
208 "VPN,ZONE,DBUS-PATH,CON-PATH,SPEC-OBJECT,MASTER-PATH"
210 /* IP group is handled by common.c */
212 /* Available fields for VPN group */
213 static NmcOutputField nmc_fields_con_active_details_vpn[] = {
214 {"GROUP", N_("GROUP")}, /* 0 */
215 {"TYPE", N_("TYPE")}, /* 1 */
216 {"USERNAME", N_("USERNAME")}, /* 2 */
217 {"GATEWAY", N_("GATEWAY")}, /* 3 */
218 {"BANNER", N_("BANNER")}, /* 4 */
219 {"VPN-STATE", N_("VPN-STATE")}, /* 5 */
220 {"CFG", N_("CFG")}, /* 6 */
223 #define NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG"
225 /* defined in common.c */
226 extern NmcOutputField nmc_fields_ip4_config[];
227 extern NmcOutputField nmc_fields_ip6_config[];
228 extern NmcOutputField nmc_fields_dhcp4_config[];
229 extern NmcOutputField nmc_fields_dhcp6_config[];
231 /* Available fields for 'connection show <con>' - active part */
232 static NmcOutputField nmc_fields_con_active_details_groups[] = {
233 {"GENERAL", N_("GENERAL"), 0, nmc_fields_con_active_details_general + 1}, /* 0 */
234 {"IP4", N_("IP4"), 0, nmc_fields_ip4_config + 1 }, /* 1 */
235 {"DHCP4", N_("DHCP4"), 0, nmc_fields_dhcp4_config + 1 }, /* 2 */
236 {"IP6", N_("IP6"), 0, nmc_fields_ip6_config + 1 }, /* 3 */
237 {"DHCP6", N_("DHCP6"), 0, nmc_fields_dhcp6_config + 1 }, /* 4 */
238 {"VPN", N_("VPN"), 0, nmc_fields_con_active_details_vpn + 1 }, /* 5 */
239 {NULL, NULL, 0, NULL}
241 #define NMC_FIELDS_CON_ACTIVE_DETAILS_ALL "GENERAL,IP4,DHCP4,IP6,DHCP6,VPN"
243 /* Pseudo group names for 'connection show <con>' */
244 /* e.g.: nmcli -f profile con show my-eth0 */
245 /* e.g.: nmcli -f active con show my-eth0 */
246 #define CON_SHOW_DETAIL_GROUP_PROFILE "profile"
247 #define CON_SHOW_DETAIL_GROUP_ACTIVE "active"
249 /* glib main loop variable - defined in nmcli.c */
250 extern GMainLoop *loop;
252 static guint progress_id = 0; /* ID of event source for displaying progress */
254 /* for readline TAB completion in editor */
258 NMConnection *connection;
260 const char *property;
262 static TabCompletionInfo nmc_tab_completion = {NULL, NULL, NULL, NULL};
264 /* Global variable defined in nmcli.c - used for TAB completion */
267 static char *gen_connection_types (const char *text, int state);
272 g_printerr (_("Usage: nmcli connection { COMMAND | help }\n\n"
273 "COMMAND := { show | up | down | add | modify | edit | delete | monitor | reload | load }\n\n"
274 " show [--active] [--order <order spec>]\n"
275 " show [--active] [id | uuid | path | apath] <ID> ...\n\n"
276 " up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>] [passwd-file <file with passwords>]\n\n"
277 " down [id | uuid | path | apath] <ID> ...\n\n"
278 " add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- ([+|-]<setting>.<property> <value>)+]\n\n"
279 " modify [--temporary] [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n\n"
280 " clone [--temporary] [id | uuid | path ] <ID> <new name>\n\n"
281 " edit [id | uuid | path] <ID>\n"
282 " edit [type <new_con_type>] [con-name <new_con_name>]\n\n"
283 " delete [id | uuid | path] <ID>\n\n"
284 " monitor [id | uuid | path] <ID> ...\n\n"
286 " load <filename> [ <filename>... ]\n\n"
287 " import [--temporary] type <type> file <file to import>\n\n"
288 " export [id | uuid | path] <ID> [<output file>]\n\n"));
292 usage_connection_show (void)
294 g_printerr (_("Usage: nmcli connection show { ARGUMENTS | help }\n"
296 "ARGUMENTS := [--active] [--order <order spec>]\n"
298 "List in-memory and on-disk connection profiles, some of which may also be\n"
299 "active if a device is using that connection profile. Without a parameter, all\n"
300 "profiles are listed. When --active option is specified, only the active\n"
301 "profiles are shown. --order allows custom connection ordering (see manual page).\n"
303 "ARGUMENTS := [--active] [id | uuid | path | apath] <ID> ...\n"
305 "Show details for specified connections. By default, both static configuration\n"
306 "and active connection data are displayed. It is possible to filter the output\n"
307 "using global '--fields' option. Refer to the manual page for more information.\n"
308 "When --active option is specified, only the active profiles are taken into\n"
309 "account. Use global --show-secrets option to reveal associated secrets as well.\n"));
313 usage_connection_up (void)
315 g_printerr (_("Usage: nmcli connection up { ARGUMENTS | help }\n"
317 "ARGUMENTS := [id | uuid | path] <ID> [ifname <ifname>] [ap <BSSID>] [nsp <name>] [passwd-file <file with passwords>]\n"
319 "Activate a connection on a device. The profile to activate is identified by its\n"
320 "name, UUID or D-Bus path.\n"
322 "ARGUMENTS := ifname <ifname> [ap <BSSID>] [nsp <name>] [passwd-file <file with passwords>]\n"
324 "Activate a device with a connection. The connection profile is selected\n"
325 "automatically by NetworkManager.\n"
327 "ifname - specifies the device to active the connection on\n"
328 "ap - specifies AP to connect to (only valid for Wi-Fi)\n"
329 "nsp - specifies NSP to connect to (only valid for WiMAX)\n"
330 "passwd-file - file with password(s) required to activate the connection\n\n"));
334 usage_connection_down (void)
336 g_printerr (_("Usage: nmcli connection down { ARGUMENTS | help }\n"
338 "ARGUMENTS := [id | uuid | path | apath] <ID> ...\n"
340 "Deactivate a connection from a device (without preventing the device from\n"
341 "further auto-activation). The profile to deactivate is identified by its name,\n"
342 "UUID or D-Bus path.\n\n"));
346 usage_connection_add (void)
348 g_printerr (_("Usage: nmcli connection add { ARGUMENTS | help }\n"
350 "ARGUMENTS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- ([+|-]<setting>.<property> <value>)+]\n\n"
353 " ifname <interface name> | \"*\"\n"
354 " [con-name <connection name>]\n"
355 " [autoconnect yes|no]\n"
357 " [master <master (ifname, or connection UUID or name)>]\n"
358 " [slave-type <master connection type>]\n\n"
359 " TYPE_SPECIFIC_OPTIONS:\n"
360 " ethernet: [mac <MAC address>]\n"
361 " [cloned-mac <cloned MAC address>]\n"
363 " wifi: ssid <SSID>\n"
364 " [mac <MAC address>]\n"
365 " [cloned-mac <cloned MAC address>]\n"
367 " [mode infrastructure|ap|adhoc]\n\n"
368 " wimax: [mac <MAC address>]\n"
370 " pppoe: username <PPPoE username>\n"
371 " [password <PPPoE password>]\n"
372 " [service <PPPoE service name>]\n"
374 " [mac <MAC address>]\n\n"
376 " [user <username>]\n"
377 " [password <password>]\n\n"
378 " cdma: [user <username>]\n"
379 " [password <password>]\n\n"
380 " infiniband: [mac <MAC address>]\n"
382 " [transport-mode datagram | connected]\n"
383 " [parent <ifname>]\n"
384 " [p-key <IPoIB P_Key>]\n\n"
385 " bluetooth: [addr <bluetooth address>]\n"
386 " [bt-type panu|dun-gsm|dun-cdma]\n\n"
387 " vlan: dev <parent device (connection UUID, ifname, or MAC)>\n"
389 " [flags <VLAN flags>]\n"
390 " [ingress <ingress priority mapping>]\n"
391 " [egress <egress priority mapping>]\n"
393 " bond: [mode balance-rr (0) | active-backup (1) | balance-xor (2) | broadcast (3) |\n"
394 " 802.3ad (4) | balance-tlb (5) | balance-alb (6)]\n"
395 " [primary <ifname>]\n"
397 " [downdelay <num>]\n"
399 " [arp-interval <num>]\n"
400 " [arp-ip-target <num>]\n"
401 " [lacp-rate slow (0) | fast (1)]\n\n"
402 " bond-slave: master <master (ifname, or connection UUID or name)>\n\n"
403 " team: [config <file>|<raw JSON data>]\n\n"
404 " team-slave: master <master (ifname, or connection UUID or name)>\n"
405 " [config <file>|<raw JSON data>]\n\n"
406 " bridge: [stp yes|no]\n"
407 " [priority <num>]\n"
408 " [forward-delay <2-30>]\n"
409 " [hello-time <1-10>]\n"
410 " [max-age <6-40>]\n"
411 " [ageing-time <0-1000000>]\n"
412 " [multicast-snooping yes|no]\n"
413 " [mac <MAC address>]\n\n"
414 " bridge-slave: master <master (ifname, or connection UUID or name)>\n"
415 " [priority <0-63>]\n"
416 " [path-cost <1-65535>]\n"
417 " [hairpin yes|no]\n\n"
418 " vpn: vpn-type vpnc|openvpn|pptp|openconnect|openswan|libreswan|ssh|l2tp|iodine|...\n"
419 " [user <username>]\n\n"
420 " olpc-mesh: ssid <SSID>\n"
421 " [channel <1-13>]\n"
422 " [dhcp-anycast <MAC address>]\n\n"
423 " adsl: username <username>\n"
424 " protocol pppoa|pppoe|ipoatm\n"
425 " [password <password>]\n"
426 " [encapsulation vcmux|llc]\n\n"
427 " tun: mode tun|tap\n"
431 " [vnet-hdr yes|no]\n"
432 " [multi-queue yes|no]\n\n"
433 " ip-tunnel: mode ipip|gre|sit|isatap|vti|ip6ip6|ipip6|ip6gre|vti6\n"
434 " remote <remote endpoint IP>\n"
435 " [local <local endpoint IP>]\n"
436 " [dev <parent device (ifname or connection UUID)>]\n\n"
437 " macvlan: dev <parent device (connection UUID, ifname, or MAC)>\n"
438 " mode vepa|bridge|private|passthru|source\n"
440 " vxlan: id <VXLAN ID>\n"
441 " remote <IP of multicast group or remote address>\n"
442 " [local <source IP>]\n"
443 " [dev <parent device (ifname or connection UUID)>]\n"
444 " [source-port-min <0-65535>]\n"
445 " [source-port-max <0-65535>]\n"
446 " [destination-port <0-65535>]\n\n"
448 " bridge: [priority <0-63>]\n"
449 " [path-cost <1-65535>]\n"
450 " [hairpin yes|no]\n\n"
451 " team: [config <file>|<raw JSON data>]\n\n"
453 " [ip4 <IPv4 address>] [gw4 <IPv4 gateway>]\n"
454 " [ip6 <IPv6 address>] [gw6 <IPv6 gateway>]\n\n"));
458 usage_connection_modify (void)
460 g_printerr (_("Usage: nmcli connection modify { ARGUMENTS | help }\n"
462 "ARGUMENTS := [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n"
464 "Modify one or more properties of the connection profile.\n"
465 "The profile is identified by its name, UUID or D-Bus path. For multi-valued\n"
466 "properties you can use optional '+' or '-' prefix to the property name.\n"
467 "The '+' sign allows appending items instead of overwriting the whole value.\n"
468 "The '-' sign allows removing selected items instead of the whole value.\n"
471 "nmcli con mod home-wifi wifi.ssid rakosnicek\n"
472 "nmcli con mod em1-1 ipv4.method manual ipv4.addr \"192.168.1.2/24, 10.10.1.5/8\"\n"
473 "nmcli con mod em1-1 +ipv4.dns 8.8.4.4\n"
474 "nmcli con mod em1-1 -ipv4.dns 1\n"
475 "nmcli con mod em1-1 -ipv6.addr \"abbe::cafe/56\"\n"
476 "nmcli con mod bond0 +bond.options mii=500\n"
477 "nmcli con mod bond0 -bond.options downdelay\n\n"));
481 usage_connection_clone (void)
483 g_printerr (_("Usage: nmcli connection clone { ARGUMENTS | help }\n"
485 "ARGUMENTS := [--temporary] [id | uuid | path] <ID> <new name>\n"
487 "Clone an existing connection profile. The newly created connection will be\n"
488 "the exact copy of the <ID>, except the uuid property (will be generated) and\n"
489 "id (provided as <new name> argument).\n\n"));
493 usage_connection_edit (void)
495 g_printerr (_("Usage: nmcli connection edit { ARGUMENTS | help }\n"
497 "ARGUMENTS := [id | uuid | path] <ID>\n"
499 "Edit an existing connection profile in an interactive editor.\n"
500 "The profile is identified by its name, UUID or D-Bus path\n"
502 "ARGUMENTS := [type <new connection type>] [con-name <new connection name>]\n"
504 "Add a new connection profile in an interactive editor.\n\n"));
508 usage_connection_delete (void)
510 g_printerr (_("Usage: nmcli connection delete { ARGUMENTS | help }\n"
512 "ARGUMENTS := [id | uuid | path] <ID>\n"
514 "Delete a connection profile.\n"
515 "The profile is identified by its name, UUID or D-Bus path.\n\n"));
519 usage_connection_monitor (void)
521 g_printerr (_("Usage: nmcli connection monitor { ARGUMENTS | help }\n"
523 "ARGUMENTS := [id | uuid | path] <ID> ...\n"
525 "Monitor connection profile activity.\n"
526 "This command prints a line whenever the specified connection changes.\n"
527 "Monitors all connection profiles in case none is specified.\n\n"));
531 usage_connection_reload (void)
533 g_printerr (_("Usage: nmcli connection reload { help }\n"
535 "Reload all connection files from disk.\n\n"));
539 usage_connection_load (void)
541 g_printerr (_("Usage: nmcli connection load { ARGUMENTS | help }\n"
543 "ARGUMENTS := <filename> [<filename>...]\n"
545 "Load/reload one or more connection files from disk. Use this after manually\n"
546 "editing a connection file to ensure that NetworkManager is aware of its latest\n"
551 usage_connection_import (void)
553 g_printerr (_("Usage: nmcli connection import { ARGUMENTS | help }\n"
555 "ARGUMENTS := [--temporary] type <type> file <file to import>\n"
557 "Import an external/foreign configuration as a NetworkManager connection profile.\n"
558 "The type of the input file is specified by type option.\n"
559 "Only VPN configurations are supported at the moment. The configuration\n"
560 "is imported by NetworkManager VPN plugins.\n\n"));
564 usage_connection_export (void)
566 g_printerr (_("Usage: nmcli connection export { ARGUMENTS | help }\n"
568 "ARGUMENTS := [id | uuid | path] <ID> [<output file>]\n"
570 "Export a connection. Only VPN connections are supported at the moment.\n"
571 "The data are directed to standard output or to a file if a name is given.\n\n"));
575 usage_connection_second_level (const char *cmd)
579 if (matches (cmd, "show") == 0)
580 usage_connection_show ();
581 else if (matches (cmd, "up") == 0)
582 usage_connection_up ();
583 else if (matches (cmd, "down") == 0)
584 usage_connection_down ();
585 else if (matches (cmd, "add") == 0)
586 usage_connection_add ();
587 else if (matches (cmd, "modify") == 0)
588 usage_connection_modify ();
589 else if (matches (cmd, "clone") == 0)
590 usage_connection_clone ();
591 else if (matches (cmd, "edit") == 0)
592 usage_connection_edit ();
593 else if (matches (cmd, "delete") == 0)
594 usage_connection_delete ();
595 else if (matches (cmd, "monitor") == 0)
596 usage_connection_monitor ();
597 else if (matches (cmd, "reload") == 0)
598 usage_connection_reload ();
599 else if (matches (cmd, "load") == 0)
600 usage_connection_load ();
601 else if (matches (cmd, "import") == 0)
602 usage_connection_import ();
603 else if (matches (cmd, "export") == 0)
604 usage_connection_export ();
615 g_source_remove (progress_id);
617 nmc_terminal_erase_line ();
620 g_main_loop_quit (loop); /* quit main loop */
624 construct_header_name (const char *base, const char *spec)
626 static char header_name[128];
631 g_strlcpy (header_name, base, sizeof (header_name));
632 g_strlcat (header_name, " (", sizeof (header_name));
633 g_strlcat (header_name, spec, sizeof (header_name));
634 g_strlcat (header_name, ")", sizeof (header_name));
640 active_connection_state_to_string (NMActiveConnectionState state)
643 case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
644 return _("activating");
645 case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
646 return _("activated");
647 case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING:
648 return _("deactivating");
649 case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED:
650 return _("deactivated");
651 case NM_ACTIVE_CONNECTION_STATE_UNKNOWN:
658 vpn_connection_state_to_string (NMVpnConnectionState state)
661 case NM_VPN_CONNECTION_STATE_PREPARE:
662 return _("VPN connecting (prepare)");
663 case NM_VPN_CONNECTION_STATE_NEED_AUTH:
664 return _("VPN connecting (need authentication)");
665 case NM_VPN_CONNECTION_STATE_CONNECT:
666 return _("VPN connecting");
667 case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
668 return _("VPN connecting (getting IP configuration)");
669 case NM_VPN_CONNECTION_STATE_ACTIVATED:
670 return _("VPN connected");
671 case NM_VPN_CONNECTION_STATE_FAILED:
672 return _("VPN connection failed");
673 case NM_VPN_CONNECTION_STATE_DISCONNECTED:
674 return _("VPN disconnected");
680 /* Caller has to free the returned string */
682 get_ac_device_string (NMActiveConnection *active)
685 const GPtrArray *devices;
691 /* Get devices of the active connection */
692 dev_str = g_string_new (NULL);
693 devices = nm_active_connection_get_devices (active);
694 for (i = 0; i < devices->len; i++) {
695 NMDevice *device = g_ptr_array_index (devices, i);
696 const char *dev_iface = nm_device_get_iface (device);
699 g_string_append (dev_str, dev_iface);
700 g_string_append_c (dev_str, ',');
703 if (dev_str->len > 0)
704 g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */
706 return g_string_free (dev_str, FALSE);
709 static NMActiveConnection *
710 get_ac_for_connection (const GPtrArray *active_cons, NMConnection *connection)
712 const char *con_path, *ac_con_path;
714 NMActiveConnection *ac = NULL;
716 /* Is the connection active? */
717 con_path = nm_connection_get_path (connection);
718 for (i = 0; i < active_cons->len; i++) {
719 NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
720 NMRemoteConnection *con;
722 con = nm_active_connection_get_connection (candidate);
723 ac_con_path = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL;
724 if (!g_strcmp0 (ac_con_path, con_path)) {
732 /* Put secrets into local connection. */
734 update_secrets_in_connection (NMRemoteConnection *remote, NMConnection *local)
738 GError *error = NULL;
740 for (i = 0; nmc_fields_settings_names[i].name; i++) {
741 secrets = nm_remote_connection_get_secrets (remote, nmc_fields_settings_names[i].name, NULL, NULL);
743 if (!nm_connection_update_secrets (local, NULL, secrets, &error) && error) {
744 g_printerr (_("Error updating secrets for %s: %s\n"),
745 nmc_fields_settings_names[i].name,
747 g_clear_error (&error);
749 g_variant_unref (secrets);
755 nmc_connection_profile_details (NMConnection *connection, NmCli *nmc, gboolean secrets)
757 GError *error = NULL;
758 GArray *print_settings_array;
759 GPtrArray *prop_array = NULL;
762 char *fields_all = NMC_FIELDS_SETTINGS_NAMES_ALL;
763 char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL;
764 const char *base_hdr = _("Connection profile details");
765 gboolean was_output = FALSE;
767 if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
768 fields_str = fields_common;
769 else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
770 fields_str = fields_all;
772 fields_str = nmc->required_fields;
774 print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, TRUE, &prop_array, &error);
776 g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message);
777 g_error_free (error);
778 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
781 g_assert (print_settings_array);
784 nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_connection_get_id (connection));
785 nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL,
786 nmc_fields_settings_names, FALSE, NULL, NULL);
788 nmc_fields_settings_names[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
789 print_required_fields (nmc, nmc_fields_settings_names);
791 /* Loop through the required settings and print them. */
792 for (i = 0; i < print_settings_array->len; i++) {
794 int section_idx = g_array_index (print_settings_array, int, i);
795 const char *prop_name = (const char *) g_ptr_array_index (prop_array, i);
797 if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
798 g_print ("\n"); /* Empty line */
802 /* Remove any previous data */
803 nmc_empty_output_fields (nmc);
805 setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name);
807 setting_details (setting, nmc, prop_name, secrets);
813 g_array_free (print_settings_array, TRUE);
815 g_ptr_array_free (prop_array, TRUE);
820 static NMActiveConnection *
821 find_active_connection (const GPtrArray *active_cons,
822 const GPtrArray *cons,
823 const char *filter_type,
824 const char *filter_val,
828 int start = (idx && *idx > 0) ? *idx : 0;
829 const char *path, *a_path, *path_num, *a_path_num;
832 NMRemoteConnection *con;
833 NMActiveConnection *found = NULL;
835 for (i = start; i < active_cons->len; i++) {
836 NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
838 con = nm_active_connection_get_connection (candidate);
840 id = nm_active_connection_get_id (candidate);
841 uuid = nm_active_connection_get_uuid (candidate);
842 path = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL;
843 path_num = path ? strrchr (path, '/') + 1 : NULL;
844 a_path = nm_object_get_path (NM_OBJECT (candidate));
845 a_path_num = a_path ? strrchr (a_path, '/') + 1 : NULL;
847 /* When filter_type is NULL, compare connection ID (filter_val)
848 * against all types. Otherwise, only compare against the specific
849 * type. If 'path' or 'apath' filter types are specified, comparison
850 * against numeric index (in addition to the whole path) is allowed.
852 if ( ( (!filter_type || strcmp (filter_type, "id") == 0)
853 && strcmp (filter_val, id) == 0)
854 || ( (!filter_type || strcmp (filter_type, "uuid") == 0)
855 && strcmp (filter_val, uuid) == 0)
856 || ( (!filter_type || strcmp (filter_type, "path") == 0)
857 && (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)))
858 || ( (!filter_type || strcmp (filter_type, "apath") == 0)
859 && (g_strcmp0 (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0)))) {
876 fill_output_connection (NMConnection *connection, NmCli *nmc, gboolean active_only)
878 NMSettingConnection *s_con;
880 time_t timestamp_real;
882 char *timestamp_real_str = "";
885 NMActiveConnection *ac = NULL;
886 const char *ac_path = NULL;
887 const char *ac_state = NULL;
888 NMActiveConnectionState ac_state_int = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
891 s_con = nm_connection_get_setting_connection (connection);
894 ac = get_ac_for_connection (nm_client_get_active_connections (nmc->client), connection);
895 if (active_only && !ac)
899 ac_path = nm_object_get_path (NM_OBJECT (ac));
900 ac_state_int = nm_active_connection_get_state (ac);
901 ac_state = active_connection_state_to_string (ac_state_int);
902 ac_dev = get_ac_device_string (ac);
905 /* Obtain field values */
906 timestamp = nm_setting_connection_get_timestamp (s_con);
907 timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
909 timestamp_real = timestamp;
910 timestamp_real_str = g_malloc0 (64);
911 strftime (timestamp_real_str, 64, "%c", localtime (×tamp_real));
913 prio_str = g_strdup_printf ("%u", nm_setting_connection_get_autoconnect_priority (s_con));
915 arr = nmc_dup_fields_array (nmc_fields_con_show,
916 sizeof (nmc_fields_con_show),
918 /* Show active connections in color */
920 if (ac_state_int == NM_ACTIVE_CONNECTION_STATE_ACTIVATING)
921 set_val_color_all (arr, NMC_TERM_COLOR_YELLOW);
922 else if (ac_state_int == NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
923 set_val_color_all (arr, NMC_TERM_COLOR_GREEN);
924 else if (ac_state_int > NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
925 set_val_color_all (arr, NMC_TERM_COLOR_RED);
928 set_val_strc (arr, 0, nm_setting_connection_get_id (s_con));
929 set_val_strc (arr, 1, nm_setting_connection_get_uuid (s_con));
930 set_val_strc (arr, 2, nm_setting_connection_get_connection_type (s_con));
931 set_val_str (arr, 3, timestamp_str);
932 set_val_str (arr, 4, timestamp ? timestamp_real_str : g_strdup (_("never")));
933 set_val_strc (arr, 5, nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no"));
934 set_val_str (arr, 6, prio_str);
935 set_val_strc (arr, 7, nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no"));
936 set_val_strc (arr, 8, nm_connection_get_path (connection));
937 set_val_strc (arr, 9, ac ? _("yes") : _("no"));
938 set_val_str (arr, 10, ac_dev);
939 set_val_strc (arr, 11, ac_state);
940 set_val_strc (arr, 12, ac_path);
942 g_ptr_array_add (nmc->output_data, arr);
946 fill_output_connection_for_invisible (NMActiveConnection *ac, NmCli *nmc)
949 const char *ac_path = NULL;
950 const char *ac_state = NULL;
951 char *name, *ac_dev = NULL;
953 name = g_strdup_printf ("<invisible> %s", nm_active_connection_get_id (ac));
954 ac_path = nm_object_get_path (NM_OBJECT (ac));
955 ac_state = active_connection_state_to_string (nm_active_connection_get_state (ac));
956 ac_dev = get_ac_device_string (ac);
958 arr = nmc_dup_fields_array (nmc_fields_con_show,
959 sizeof (nmc_fields_con_show),
962 set_val_str (arr, 0, name);
963 set_val_strc (arr, 1, nm_active_connection_get_uuid (ac));
964 set_val_strc (arr, 2, nm_active_connection_get_connection_type (ac));
965 set_val_strc (arr, 3, NULL);
966 set_val_strc (arr, 4, NULL);
967 set_val_strc (arr, 5, NULL);
968 set_val_strc (arr, 6, NULL);
969 set_val_strc (arr, 7, NULL);
970 set_val_strc (arr, 8, NULL);
971 set_val_strc (arr, 9, _("yes"));
972 set_val_str (arr, 10, ac_dev);
973 set_val_strc (arr, 11, ac_state);
974 set_val_strc (arr, 12, ac_path);
976 set_val_color_fmt_all (arr, NMC_TERM_FORMAT_DIM);
978 g_ptr_array_add (nmc->output_data, arr);
982 fill_output_active_connection (NMActiveConnection *active,
987 NMRemoteConnection *con;
988 NMSettingConnection *s_con;
989 const GPtrArray *devices;
991 NMActiveConnectionState state;
993 const char *con_path = NULL, *con_zone = NULL;
995 NmcOutputField *tmpl, *arr;
997 int idx_start = with_group ? 0 : 1;
999 con = nm_active_connection_get_connection (active);
1001 con_path = nm_connection_get_path (NM_CONNECTION (con));
1002 s_con = nm_connection_get_setting_connection (NM_CONNECTION (con));
1003 g_assert (s_con != NULL);
1004 con_zone = nm_setting_connection_get_zone (s_con);
1007 state = nm_active_connection_get_state (active);
1008 master = nm_active_connection_get_master (active);
1010 /* Get devices of the active connection */
1011 dev_str = g_string_new (NULL);
1012 devices = nm_active_connection_get_devices (active);
1013 for (i = 0; i < devices->len; i++) {
1014 NMDevice *device = g_ptr_array_index (devices, i);
1015 const char *dev_iface = nm_device_get_iface (device);
1018 g_string_append (dev_str, dev_iface);
1019 g_string_append_c (dev_str, ',');
1022 if (dev_str->len > 0)
1023 g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */
1025 tmpl = nmc_fields_con_active_details_general;
1026 tmpl_len = sizeof (nmc_fields_con_active_details_general);
1029 tmpl_len -= sizeof (NmcOutputField);
1032 /* Fill field values */
1033 arr = nmc_dup_fields_array (tmpl, tmpl_len, o_flags);
1035 set_val_strc (arr, 0, nmc_fields_con_active_details_groups[0].name);
1036 set_val_strc (arr, 1-idx_start, nm_active_connection_get_id (active));
1037 set_val_strc (arr, 2-idx_start, nm_active_connection_get_uuid (active));
1038 set_val_str (arr, 3-idx_start, dev_str->str);
1039 set_val_strc (arr, 4-idx_start, active_connection_state_to_string (state));
1040 set_val_strc (arr, 5-idx_start, nm_active_connection_get_default (active) ? _("yes") : _("no"));
1041 set_val_strc (arr, 6-idx_start, nm_active_connection_get_default6 (active) ? _("yes") : _("no"));
1042 set_val_strc (arr, 7-idx_start, nm_active_connection_get_specific_object_path (active));
1043 set_val_strc (arr, 8-idx_start, NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no"));
1044 set_val_strc (arr, 9-idx_start, nm_object_get_path (NM_OBJECT (active)));
1045 set_val_strc (arr, 10-idx_start, con_path);
1046 set_val_strc (arr, 11-idx_start, con_zone);
1047 set_val_strc (arr, 12-idx_start, master ? nm_object_get_path (NM_OBJECT (master)) : NULL);
1049 g_ptr_array_add (nmc->output_data, arr);
1051 g_string_free (dev_str, FALSE);
1060 fill_vpn_data_item (const char *key, const char *value, gpointer user_data)
1062 FillVPNDataInfo *info = (FillVPNDataInfo *) user_data;
1064 info->array[info->idx++] = g_strdup_printf ("%s = %s", key, value);
1067 // FIXME: The same or similar code for VPN info appears also in nm-applet (applet-dialogs.c),
1068 // and in gnome-control-center as well. It could probably be shared somehow.
1070 get_vpn_connection_type (NMConnection *connection)
1072 const char *type, *p;
1074 /* The service type is in form of "org.freedesktop.NetworkManager.vpnc".
1075 * Extract end part after last dot, e.g. "vpnc"
1077 type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
1078 p = strrchr (type, '.');
1079 return g_strdup (p ? p + 1 : type);
1082 /* VPN parameters can be found at:
1083 * http://git.gnome.org/browse/network-manager-openvpn/tree/src/nm-openvpn-service.h
1084 * http://git.gnome.org/browse/network-manager-vpnc/tree/src/nm-vpnc-service.h
1085 * http://git.gnome.org/browse/network-manager-pptp/tree/src/nm-pptp-service.h
1086 * http://git.gnome.org/browse/network-manager-openconnect/tree/src/nm-openconnect-service.h
1087 * http://git.gnome.org/browse/network-manager-openswan/tree/src/nm-openswan-service.h
1088 * See also 'properties' directory in these plugins.
1090 static const gchar *
1091 find_vpn_gateway_key (const char *vpn_type)
1093 if (g_strcmp0 (vpn_type, "openvpn") == 0) return "remote";
1094 if (g_strcmp0 (vpn_type, "vpnc") == 0) return "IPSec gateway";
1095 if (g_strcmp0 (vpn_type, "pptp") == 0) return "gateway";
1096 if (g_strcmp0 (vpn_type, "openconnect") == 0) return "gateway";
1097 if (g_strcmp0 (vpn_type, "openswan") == 0) return "right";
1098 if (g_strcmp0 (vpn_type, "libreswan") == 0) return "right";
1099 if (g_strcmp0 (vpn_type, "ssh") == 0) return "remote";
1100 if (g_strcmp0 (vpn_type, "l2tp") == 0) return "gateway";
1104 static const gchar *
1105 find_vpn_username_key (const char *vpn_type)
1107 if (g_strcmp0 (vpn_type, "openvpn") == 0) return "username";
1108 if (g_strcmp0 (vpn_type, "vpnc") == 0) return "Xauth username";
1109 if (g_strcmp0 (vpn_type, "pptp") == 0) return "user";
1110 if (g_strcmp0 (vpn_type, "openconnect") == 0) return "username";
1111 if (g_strcmp0 (vpn_type, "openswan") == 0) return "leftxauthusername";
1112 if (g_strcmp0 (vpn_type, "libreswan") == 0) return "leftxauthusername";
1113 if (g_strcmp0 (vpn_type, "l2tp") == 0) return "user";
1118 VPN_DATA_ITEM_GATEWAY,
1119 VPN_DATA_ITEM_USERNAME
1122 static const gchar *
1123 get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item)
1126 char *type = get_vpn_connection_type (connection);
1128 switch (vpn_data_item) {
1129 case VPN_DATA_ITEM_GATEWAY:
1130 key = find_vpn_gateway_key (type);
1132 case VPN_DATA_ITEM_USERNAME:
1133 key = find_vpn_username_key (type);
1141 return nm_setting_vpn_get_data_item (nm_connection_get_setting_vpn (connection), key);
1146 nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
1148 GError *error = NULL;
1149 GArray *print_groups;
1150 GPtrArray *group_fields = NULL;
1153 char *fields_all = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
1154 char *fields_common = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
1155 NmcOutputField *tmpl, *arr;
1157 const char *base_hdr = _("Activate connection details");
1158 gboolean was_output = FALSE;
1160 if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
1161 fields_str = fields_common;
1162 else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
1163 fields_str = fields_all;
1165 fields_str = nmc->required_fields;
1167 print_groups = parse_output_fields (fields_str, nmc_fields_con_active_details_groups, TRUE, &group_fields, &error);
1169 g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message);
1170 g_error_free (error);
1171 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1174 g_assert (print_groups);
1177 nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_active_connection_get_uuid (acon));
1178 nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_ALL,
1179 nmc_fields_con_active_details_groups, FALSE, NULL, NULL);
1181 nmc_fields_con_active_details_groups[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
1182 print_required_fields (nmc, nmc_fields_con_active_details_groups);
1184 /* Loop through the groups and print them. */
1185 for (i = 0; i < print_groups->len; i++) {
1186 int group_idx = g_array_index (print_groups, int, i);
1187 char *group_fld = (char *) g_ptr_array_index (group_fields, i);
1189 if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
1190 g_print ("\n"); /* Empty line */
1194 /* Remove any previous data */
1195 nmc_empty_output_fields (nmc);
1198 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[0].name) == 0) {
1199 /* Add field names */
1200 tmpl = nmc_fields_con_active_details_general;
1201 tmpl_len = sizeof (nmc_fields_con_active_details_general);
1202 nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL,
1203 tmpl, FALSE, NULL, NULL);
1204 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
1205 g_ptr_array_add (nmc->output_data, arr);
1207 /* Fill in values */
1208 fill_output_active_connection (acon, nmc, TRUE, NMC_OF_FLAG_SECTION_PREFIX);
1210 print_data (nmc); /* Print all data */
1216 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[1].name) == 0) {
1217 gboolean b1 = FALSE;
1218 NMIPConfig *cfg4 = nm_active_connection_get_ip4_config (acon);
1220 b1 = print_ip4_config (cfg4, nmc, "IP4", group_fld);
1221 was_output = was_output || b1;
1225 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[2].name) == 0) {
1226 gboolean b1 = FALSE;
1227 NMDhcpConfig *dhcp4 = nm_active_connection_get_dhcp4_config (acon);
1229 b1 = print_dhcp4_config (dhcp4, nmc, "DHCP4", group_fld);
1230 was_output = was_output || b1;
1234 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[3].name) == 0) {
1235 gboolean b1 = FALSE;
1236 NMIPConfig *cfg6 = nm_active_connection_get_ip6_config (acon);
1238 b1 = print_ip6_config (cfg6, nmc, "IP6", group_fld);
1239 was_output = was_output || b1;
1243 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[4].name) == 0) {
1244 gboolean b1 = FALSE;
1245 NMDhcpConfig *dhcp6 = nm_active_connection_get_dhcp6_config (acon);
1247 b1 = print_dhcp6_config (dhcp6, nmc, "DHCP6", group_fld);
1248 was_output = was_output || b1;
1252 if (NM_IS_VPN_CONNECTION (acon) &&
1253 strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[5].name) == 0) {
1255 NMSettingConnection *s_con;
1256 NMSettingVpn *s_vpn;
1257 NMVpnConnectionState vpn_state;
1258 char *type_str, *banner_str = NULL, *vpn_state_str;
1260 const char *username = NULL;
1261 char **vpn_data_array = NULL;
1264 con = NM_CONNECTION (nm_active_connection_get_connection (acon));
1266 s_con = nm_connection_get_setting_connection (con);
1267 g_assert (s_con != NULL);
1269 tmpl = nmc_fields_con_active_details_vpn;
1270 tmpl_len = sizeof (nmc_fields_con_active_details_vpn);
1271 nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL,
1272 tmpl, FALSE, NULL, NULL);
1273 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
1274 g_ptr_array_add (nmc->output_data, arr);
1276 s_vpn = nm_connection_get_setting_vpn (con);
1278 items_num = nm_setting_vpn_get_num_data_items (s_vpn);
1279 if (items_num > 0) {
1280 FillVPNDataInfo info;
1282 vpn_data_array = g_new (char *, items_num + 1);
1283 info.array = vpn_data_array;
1285 nm_setting_vpn_foreach_data_item (s_vpn, &fill_vpn_data_item, &info);
1286 vpn_data_array[items_num] = NULL;
1288 username = nm_setting_vpn_get_user_name (s_vpn);
1291 type_str = get_vpn_connection_type (con);
1292 banner = nm_vpn_connection_get_banner (NM_VPN_CONNECTION (acon));
1294 banner_str = g_strescape (banner, "");
1295 vpn_state = nm_vpn_connection_get_vpn_state (NM_VPN_CONNECTION (acon));
1296 vpn_state_str = g_strdup_printf ("%d - %s", vpn_state, vpn_connection_state_to_string (vpn_state));
1299 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX);
1300 set_val_strc (arr, 0, nmc_fields_con_active_details_groups[5].name);
1301 set_val_str (arr, 1, type_str);
1302 set_val_strc (arr, 2, username ? username : get_vpn_data_item (con, VPN_DATA_ITEM_USERNAME));
1303 set_val_strc (arr, 3, get_vpn_data_item (con, VPN_DATA_ITEM_GATEWAY));
1304 set_val_str (arr, 4, banner_str);
1305 set_val_str (arr, 5, vpn_state_str);
1306 set_val_arr (arr, 6, vpn_data_array);
1307 g_ptr_array_add (nmc->output_data, arr);
1309 print_data (nmc); /* Print all data */
1314 g_array_free (print_groups, TRUE);
1316 g_ptr_array_free (group_fields, TRUE);
1322 split_required_fields_for_con_show (const char *input,
1323 char **profile_flds,
1327 char **fields, **iter;
1329 GString *str1, *str2;
1331 gboolean group_profile = FALSE;
1332 gboolean group_active = FALSE;
1333 gboolean success = TRUE;
1334 gboolean is_all, is_common;
1338 *profile_flds = NULL;
1339 *active_flds = NULL;
1343 str1 = g_string_new (NULL);
1344 str2 = g_string_new (NULL);
1346 /* Split supplied fields string */
1347 fields = g_strsplit_set (input, ",", -1);
1348 for (iter = fields; iter && *iter; iter++) {
1350 dot = strchr (*iter, '.');
1354 is_all = !dot && strcasecmp (*iter, "all") == 0;
1355 is_common = !dot && strcasecmp (*iter, "common") == 0;
1359 for (i = 0; nmc_fields_settings_names[i].name; i++) {
1360 if ( is_all || is_common
1361 || !strcasecmp (*iter, nmc_fields_settings_names[i].name)) {
1364 g_string_append (str1, *iter);
1365 g_string_append_c (str1, ',');
1372 for (i = 0; nmc_fields_con_active_details_groups[i].name; i++) {
1373 if ( is_all || is_common
1374 || !strcasecmp (*iter, nmc_fields_con_active_details_groups[i].name)) {
1377 g_string_append (str2, *iter);
1378 g_string_append_c (str2, ',');
1386 if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_PROFILE))
1387 group_profile = TRUE;
1388 else if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_ACTIVE))
1389 group_active = TRUE;
1391 char *allowed1 = nmc_get_allowed_fields (nmc_fields_settings_names, -1);
1392 char *allowed2 = nmc_get_allowed_fields (nmc_fields_con_active_details_groups, -1);
1393 g_set_error (error, NMCLI_ERROR, 0, _("invalid field '%s'; allowed fields: %s and %s, or %s,%s"),
1394 *iter, allowed1, allowed2, CON_SHOW_DETAIL_GROUP_PROFILE, CON_SHOW_DETAIL_GROUP_ACTIVE);
1403 g_strfreev (fields);
1405 /* Handle pseudo groups: profile, active */
1406 if (success && group_profile) {
1407 if (str1->len > 0) {
1408 g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone"),
1409 CON_SHOW_DETAIL_GROUP_PROFILE);
1412 g_string_assign (str1, "all,");
1414 if (success && group_active) {
1415 if (str2->len > 0) {
1416 g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone"),
1417 CON_SHOW_DETAIL_GROUP_ACTIVE);
1420 g_string_assign (str2, "all,");
1425 g_string_truncate (str1, str1->len - 1);
1427 g_string_truncate (str2, str2->len - 1);
1428 *profile_flds = g_string_free (str1, str1->len == 0);
1429 *active_flds = g_string_free (str2, str2->len == 0);
1431 g_string_free (str1, TRUE);
1432 g_string_free (str2, TRUE);
1438 NMC_SORT_ACTIVE = 1,
1439 NMC_SORT_ACTIVE_INV = -1,
1441 NMC_SORT_NAME_INV = -2,
1443 NMC_SORT_TYPE_INV = -3,
1445 NMC_SORT_PATH_INV = -4,
1450 const GArray *order;
1454 compare_connections (gconstpointer a, gconstpointer b, gpointer user_data)
1456 NMConnection *ca = *(NMConnection **)a;
1457 NMConnection *cb = *(NMConnection **)b;
1458 NMActiveConnection *aca, *acb;
1459 NmcSortInfo *info = (NmcSortInfo *) user_data;
1460 GArray *default_order = NULL;
1461 const GArray *order;
1464 const char *tmp1, *tmp2;
1465 unsigned long tmp1_int, tmp2_int;
1468 order = info->order;
1470 NmcSortOrder def[] = { NMC_SORT_ACTIVE, NMC_SORT_NAME, NMC_SORT_PATH };
1471 int num = G_N_ELEMENTS (def);
1472 default_order = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), num);
1473 g_array_append_vals (default_order, def, num);
1474 order = default_order;
1477 for (i = 0; i < order->len; i++) {
1478 item = g_array_index (order, NmcSortOrder, i);
1480 case NMC_SORT_ACTIVE:
1481 case NMC_SORT_ACTIVE_INV:
1482 aca = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), ca);
1483 acb = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), cb);
1484 cmp = (aca && !acb) ? -1 : (!aca && acb) ? 1 : 0;
1485 if (item == NMC_SORT_ACTIVE_INV)
1489 case NMC_SORT_TYPE_INV:
1490 cmp = g_strcmp0 (nm_connection_get_connection_type (ca),
1491 nm_connection_get_connection_type (cb));
1492 if (item == NMC_SORT_TYPE_INV)
1496 case NMC_SORT_NAME_INV:
1497 cmp = g_strcmp0 (nm_connection_get_id (ca),
1498 nm_connection_get_id (cb));
1499 if (item == NMC_SORT_NAME_INV)
1503 case NMC_SORT_PATH_INV:
1504 tmp1 = nm_connection_get_path (ca);
1505 tmp2 = nm_connection_get_path (cb);
1506 tmp1 = tmp1 ? strrchr (tmp1, '/') : "0";
1507 tmp2 = tmp2 ? strrchr (tmp2, '/') : "0";
1508 nmc_string_to_uint (tmp1 ? tmp1+1 : "0", FALSE, 0, 0, &tmp1_int);
1509 nmc_string_to_uint (tmp2 ? tmp2+1 : "0", FALSE, 0, 0, &tmp2_int);
1510 cmp = (int) tmp1_int - tmp2_int;
1511 if (item == NMC_SORT_PATH_INV)
1523 g_array_unref (default_order);
1528 sort_connections (const GPtrArray *cons, NmCli *nmc, const GArray *order)
1532 NmcSortInfo compare_info;
1537 compare_info.nmc = nmc;
1538 compare_info.order = order;
1540 sorted = g_ptr_array_sized_new (cons->len);
1541 for (i = 0; i < cons->len; i++)
1542 g_ptr_array_add (sorted, cons->pdata[i]);
1543 g_ptr_array_sort_with_data (sorted, compare_connections, &compare_info);
1548 compare_ac_connections (gconstpointer a, gconstpointer b, gpointer user_data)
1550 NMActiveConnection *ca = *(NMActiveConnection **)a;
1551 NMActiveConnection *cb = *(NMActiveConnection **)b;
1554 /* Sort states first */
1555 cmp = nm_active_connection_get_state (cb) - nm_active_connection_get_state (ca);
1559 cmp = g_strcmp0 (nm_active_connection_get_id (ca),
1560 nm_active_connection_get_id (cb));
1564 return g_strcmp0 (nm_active_connection_get_connection_type (ca),
1565 nm_active_connection_get_connection_type (cb));
1569 get_invisible_active_connections (NmCli *nmc)
1571 const GPtrArray *acons;
1572 GPtrArray *invisibles;
1575 g_return_val_if_fail (nmc != NULL, NULL);
1577 invisibles = g_ptr_array_new ();
1578 acons = nm_client_get_active_connections (nmc->client);
1579 for (a = 0; a < acons->len; a++) {
1580 gboolean found = FALSE;
1581 NMActiveConnection *acon = g_ptr_array_index (acons, a);
1582 const char *a_uuid = nm_active_connection_get_uuid (acon);
1584 for (c = 0; c < nmc->connections->len; c++) {
1585 NMConnection *con = g_ptr_array_index (nmc->connections, c);
1586 const char *c_uuid = nm_connection_get_uuid (con);
1588 if (strcmp (a_uuid, c_uuid) == 0) {
1593 /* Active connection is not in connections array, add it to */
1595 g_ptr_array_add (invisibles, acon);
1597 g_ptr_array_sort_with_data (invisibles, compare_ac_connections, NULL);
1601 static NMCResultCode
1602 do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets,
1603 const GArray *order, int argc, char **argv)
1606 char *profile_flds = NULL, *active_flds = NULL;
1607 GPtrArray *invisibles, *sorted_cons;
1611 char *fields_all = NMC_FIELDS_CON_SHOW_ALL;
1612 char *fields_common = NMC_FIELDS_CON_SHOW_COMMON;
1613 NmcOutputField *tmpl, *arr;
1617 if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
1618 fields_str = fields_common;
1619 else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
1620 fields_str = fields_all;
1622 fields_str = nmc->required_fields;
1624 tmpl = nmc_fields_con_show;
1625 tmpl_len = sizeof (nmc_fields_con_show);
1626 nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &err);
1630 if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &err))
1634 nmc->print_fields.header_name = active_only ? _("NetworkManager active profiles") :
1635 _("NetworkManager connection profiles");
1636 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES);
1637 g_ptr_array_add (nmc->output_data, arr);
1639 /* There might be active connections not present in connection list
1640 * (e.g. private connections of a different user). Show them as well. */
1641 invisibles = get_invisible_active_connections (nmc);
1642 for (i = 0; i < invisibles->len; i++)
1643 fill_output_connection_for_invisible (invisibles->pdata[i], nmc);
1644 g_ptr_array_free (invisibles, TRUE);
1646 /* Sort the connections and fill the output data */
1647 sorted_cons = sort_connections (nmc->connections, nmc, order);
1648 for (i = 0; i < sorted_cons->len; i++)
1649 fill_output_connection (sorted_cons->pdata[i], nmc, active_only);
1650 g_ptr_array_free (sorted_cons, TRUE);
1652 print_data (nmc); /* Print all data */
1654 gboolean new_line = FALSE;
1655 gboolean without_fields = (nmc->required_fields == NULL);
1656 const GPtrArray *active_cons = nm_client_get_active_connections (nmc->client);
1659 /* multiline mode is default for 'connection show <ID>' */
1660 if (!nmc->mode_specified)
1661 nmc->multiline_output = TRUE;
1663 /* Split required fields into the settings and active ones. */
1664 if (!split_required_fields_for_con_show (nmc->required_fields, &profile_flds, &active_flds, &err))
1666 g_free (nmc->required_fields);
1667 nmc->required_fields = NULL;
1672 NMActiveConnection *acon = NULL;
1673 const char *selector = NULL;
1675 if ( strcmp (*argv, "id") == 0
1676 || strcmp (*argv, "uuid") == 0
1677 || strcmp (*argv, "path") == 0
1678 || strcmp (*argv, "apath") == 0) {
1680 if (next_arg (&argc, &argv) != 0) {
1681 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
1682 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1687 /* Find connection by id, uuid, path or apath */
1688 con = nmc_find_connection (nmc->connections, selector, *argv, &pos);
1690 acon = find_active_connection (active_cons, nmc->connections, selector, *argv, NULL);
1692 con = NM_CONNECTION (nm_active_connection_get_connection (acon));
1695 if (!con && !acon) {
1696 g_string_printf (nmc->return_text, _("Error: %s - no such connection profile."), *argv);
1697 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
1701 /* Print connection details:
1702 * Usually we have both static and active connection.
1703 * But when a connection is private to a user, another user
1704 * may see only the active connection.
1707 /* Filter only active connections */
1709 acon = get_ac_for_connection (active_cons, con);
1710 if (active_only && !acon) {
1711 next_arg (&argc, &argv);
1715 /* Show an empty line between connections */
1719 /* Show profile configuration */
1720 if (without_fields || profile_flds) {
1722 nmc->required_fields = profile_flds;
1724 update_secrets_in_connection (NM_REMOTE_CONNECTION (con), con);
1725 res = nmc_connection_profile_details (con, nmc, show_secrets);
1726 nmc->required_fields = NULL;
1732 /* If the profile is active, print also active details */
1733 if (without_fields || active_flds) {
1735 nmc->required_fields = active_flds;
1736 res = nmc_active_connection_details (acon, nmc);
1737 nmc->required_fields = NULL;
1744 /* Take next argument.
1745 * But for pos != NULL we have more connections of the same name,
1746 * so process the same argument again.
1749 next_arg (&argc, &argv);
1755 g_string_printf (nmc->return_text, _("Error: %s."), err->message);
1756 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1759 g_free (profile_flds);
1760 g_free (active_flds);
1761 return nmc->return_value;
1764 static NMActiveConnection *
1765 get_default_active_connection (NmCli *nmc, NMDevice **device)
1767 NMActiveConnection *default_ac = NULL;
1768 NMDevice *non_default_device = NULL;
1769 NMActiveConnection *non_default_ac = NULL;
1770 const GPtrArray *connections;
1773 g_return_val_if_fail (nmc != NULL, NULL);
1774 g_return_val_if_fail (device != NULL, NULL);
1775 g_return_val_if_fail (*device == NULL, NULL);
1777 connections = nm_client_get_active_connections (nmc->client);
1778 for (i = 0; i < connections->len; i++) {
1779 NMActiveConnection *candidate = g_ptr_array_index (connections, i);
1780 const GPtrArray *devices;
1782 devices = nm_active_connection_get_devices (candidate);
1786 if (nm_active_connection_get_default (candidate)) {
1788 *device = g_ptr_array_index (devices, 0);
1789 default_ac = candidate;
1792 if (!non_default_ac) {
1793 non_default_device = g_ptr_array_index (devices, 0);
1794 non_default_ac = candidate;
1799 /* Prefer the default connection if one exists, otherwise return the first
1800 * non-default connection.
1802 if (!default_ac && non_default_ac) {
1803 default_ac = non_default_ac;
1804 *device = non_default_device;
1809 /* Find a device to activate the connection on.
1810 * IN: connection: connection to activate
1811 * iface: device interface name to use (optional)
1812 * ap: access point to use (optional; valid just for 802-11-wireless)
1813 * nsp: Network Service Provider to use (option; valid only for wimax)
1814 * OUT: device: found device
1815 * spec_object: specific_object path of NMAccessPoint
1816 * RETURNS: TRUE when a device is found, FALSE otherwise.
1819 find_device_for_connection (NmCli *nmc,
1820 NMConnection *connection,
1825 const char **spec_object,
1828 NMSettingConnection *s_con;
1829 const char *con_type;
1832 g_return_val_if_fail (nmc != NULL, FALSE);
1833 g_return_val_if_fail (iface || ap || nsp, FALSE);
1834 g_return_val_if_fail (device != NULL && *device == NULL, FALSE);
1835 g_return_val_if_fail (spec_object != NULL && *spec_object == NULL, FALSE);
1836 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1838 s_con = nm_connection_get_setting_connection (connection);
1840 con_type = nm_setting_connection_get_connection_type (s_con);
1842 if (strcmp (con_type, NM_SETTING_VPN_SETTING_NAME) == 0) {
1843 /* VPN connections */
1844 NMActiveConnection *active = NULL;
1846 *device = nm_client_get_device_by_iface (nmc->client, iface);
1848 active = nm_device_get_active_connection (*device);
1851 g_set_error (error, NMCLI_ERROR, 0, _("no active connection on device '%s'"), iface);
1854 *spec_object = nm_object_get_path (NM_OBJECT (active));
1857 active = get_default_active_connection (nmc, device);
1859 g_set_error_literal (error, NMCLI_ERROR, 0, _("no active connection or device"));
1862 *spec_object = nm_object_get_path (NM_OBJECT (active));
1866 /* Other connections */
1867 NMDevice *found_device = NULL;
1868 const GPtrArray *devices = nm_client_get_devices (nmc->client);
1870 for (i = 0; i < devices->len && !found_device; i++) {
1871 NMDevice *dev = g_ptr_array_index (devices, i);
1874 const char *dev_iface = nm_device_get_iface (dev);
1875 if ( !g_strcmp0 (dev_iface, iface)
1876 && nm_device_connection_compatible (dev, connection, NULL)) {
1880 if (nm_device_connection_compatible (dev, connection, NULL)) {
1885 if (found_device && ap && !strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME) && NM_IS_DEVICE_WIFI (dev)) {
1886 char *bssid_up = g_ascii_strup (ap, -1);
1887 const GPtrArray *aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev));
1888 found_device = NULL; /* Mark as not found; set to the device again later, only if AP matches */
1890 for (j = 0; j < aps->len; j++) {
1891 NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j);
1892 const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap);
1894 if (!strcmp (bssid_up, candidate_bssid)) {
1896 *spec_object = nm_object_get_path (NM_OBJECT (candidate_ap));
1906 *device = found_device;
1910 g_set_error (error, NMCLI_ERROR, 0, _("device '%s' not compatible with connection '%s'"),
1911 iface, nm_setting_connection_get_id (s_con));
1913 g_set_error (error, NMCLI_ERROR, 0, _("no device found for connection '%s'"),
1914 nm_setting_connection_get_id (s_con));
1921 vpn_connection_state_reason_to_string (NMVpnConnectionStateReason reason)
1924 case NM_VPN_CONNECTION_STATE_REASON_UNKNOWN:
1925 return _("unknown reason");
1926 case NM_VPN_CONNECTION_STATE_REASON_NONE:
1928 case NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED:
1929 return _("the user was disconnected");
1930 case NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED:
1931 return _("the base network connection was interrupted");
1932 case NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED:
1933 return _("the VPN service stopped unexpectedly");
1934 case NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID:
1935 return _("the VPN service returned invalid configuration");
1936 case NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT:
1937 return _("the connection attempt timed out");
1938 case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT:
1939 return _("the VPN service did not start in time");
1940 case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED:
1941 return _("the VPN service failed to start");
1942 case NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS:
1943 return _("no valid VPN secrets");
1944 case NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED:
1945 return _("invalid VPN secrets");
1946 case NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED:
1947 return _("the connection was removed");
1949 return _("unknown");
1954 device_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data)
1956 NmCli *nmc = (NmCli *) user_data;
1957 NMActiveConnection *active;
1958 NMDeviceState state;
1959 NMActiveConnectionState ac_state;
1961 active = nm_device_get_active_connection (device);
1962 state = nm_device_get_state (device);
1964 ac_state = active ? nm_active_connection_get_state (active) : NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
1966 if (ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
1967 if (nmc->print_output == NMC_PRINT_PRETTY)
1968 nmc_terminal_erase_line ();
1969 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
1970 nm_object_get_path (NM_OBJECT (active)));
1972 } else if ( ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING
1973 && state >= NM_DEVICE_STATE_IP_CONFIG) {
1974 if (nmc->print_output == NMC_PRINT_PRETTY)
1975 nmc_terminal_erase_line ();
1976 g_print (_("Connection successfully activated (master waiting for slaves) (D-Bus active path: %s)\n"),
1977 nm_object_get_path (NM_OBJECT (active)));
1979 } else if (active && ac_state != NM_ACTIVE_CONNECTION_STATE_ACTIVATING) {
1980 g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
1981 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
1987 active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpointer user_data)
1989 NmCli *nmc = (NmCli *) user_data;
1990 NMActiveConnectionState state;
1992 state = nm_active_connection_get_state (active);
1994 if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
1995 if (nmc->print_output == NMC_PRINT_PRETTY)
1996 nmc_terminal_erase_line ();
1997 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
1998 nm_object_get_path (NM_OBJECT (active)));
1999 g_object_unref (active);
2001 } else if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
2002 g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
2003 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
2004 g_object_unref (active);
2006 } else if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) {
2007 /* activating master connection does not automatically activate any slaves, so their
2008 * active connection state will not progress beyond ACTIVATING state.
2009 * Monitor the device instead. */
2010 const GPtrArray *devices;
2013 if (nmc->secret_agent) {
2014 NMRemoteConnection *connection = nm_active_connection_get_connection (active);
2016 nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent),
2017 nm_connection_get_path (NM_CONNECTION (connection)));
2020 devices = nm_active_connection_get_devices (active);
2021 device = devices->len ? g_ptr_array_index (devices, 0) : NULL;
2023 && ( NM_IS_DEVICE_BOND (device)
2024 || NM_IS_DEVICE_TEAM (device)
2025 || NM_IS_DEVICE_BRIDGE (device))) {
2026 g_signal_handlers_disconnect_by_func (active, G_CALLBACK (active_connection_state_cb), nmc);
2027 g_signal_connect (device, "notify::" NM_DEVICE_STATE, G_CALLBACK (device_state_cb), nmc);
2029 device_state_cb (device, NULL, nmc);
2035 vpn_connection_state_cb (NMVpnConnection *vpn,
2036 NMVpnConnectionState state,
2037 NMVpnConnectionStateReason reason,
2040 NmCli *nmc = (NmCli *) user_data;
2043 case NM_VPN_CONNECTION_STATE_PREPARE:
2044 case NM_VPN_CONNECTION_STATE_NEED_AUTH:
2045 case NM_VPN_CONNECTION_STATE_CONNECT:
2046 case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
2050 case NM_VPN_CONNECTION_STATE_ACTIVATED:
2051 if (nmc->print_output == NMC_PRINT_PRETTY)
2052 nmc_terminal_erase_line ();
2053 g_print (_("VPN connection successfully activated (D-Bus active path: %s)\n"),
2054 nm_object_get_path (NM_OBJECT (vpn)));
2055 g_object_unref (vpn);
2059 case NM_VPN_CONNECTION_STATE_FAILED:
2060 case NM_VPN_CONNECTION_STATE_DISCONNECTED:
2061 g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."),
2062 vpn_connection_state_reason_to_string (reason));
2063 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
2064 g_object_unref (vpn);
2074 timeout_cb (gpointer user_data)
2076 /* Time expired -> exit nmcli */
2078 NmCli *nmc = (NmCli *) user_data;
2080 g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout);
2081 nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED;
2087 progress_cb (gpointer user_data)
2089 const char *str = (const char *) user_data;
2091 nmc_terminal_show_progress (str);
2097 progress_device_cb (gpointer user_data)
2099 NMDevice *device = (NMDevice *) user_data;
2101 nmc_terminal_show_progress (device ? nmc_device_state_to_string (nm_device_get_state (device)) : "");
2107 progress_vpn_cb (gpointer user_data)
2109 NMVpnConnection *vpn = (NMVpnConnection *) user_data;
2112 str = NM_IS_VPN_CONNECTION (vpn) ?
2113 vpn_connection_state_to_string (nm_vpn_connection_get_vpn_state (vpn)) :
2116 nmc_terminal_show_progress (str);
2124 } ActivateConnectionInfo;
2127 activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_data)
2129 ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
2130 NmCli *nmc = info->nmc;
2131 NMDevice *device = info->device;
2132 NMActiveConnection *active;
2133 NMActiveConnectionState state;
2134 const GPtrArray *ac_devs;
2135 GError *error = NULL;
2137 active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
2140 g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"),
2142 g_error_free (error);
2143 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
2146 state = nm_active_connection_get_state (active);
2148 /* device could be NULL for virtual devices. Fill it here. */
2149 ac_devs = nm_active_connection_get_devices (active);
2150 info->device = device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
2153 if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
2154 /* User doesn't want to wait or already activated */
2155 if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
2156 if (nmc->print_output == NMC_PRINT_PRETTY)
2157 nmc_terminal_erase_line ();
2158 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
2159 nm_object_get_path (NM_OBJECT (active)));
2161 g_object_unref (active);
2164 if (NM_IS_VPN_CONNECTION (active)) {
2165 /* Monitor VPN state */
2166 g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc);
2168 /* Start progress indication showing VPN states */
2169 if (nmc->print_output == NMC_PRINT_PRETTY) {
2171 g_source_remove (progress_id);
2172 progress_id = g_timeout_add (120, progress_vpn_cb, NM_VPN_CONNECTION (active));
2175 g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), nmc);
2176 active_connection_state_cb (active, NULL, nmc);
2178 /* Start progress indication showing device states */
2179 if (nmc->print_output == NMC_PRINT_PRETTY) {
2181 g_source_remove (progress_id);
2182 progress_id = g_timeout_add (120, progress_device_cb, device);
2186 /* Start timer not to loop forever when signals are not emitted */
2187 g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc);
2195 * @passwd_file: file with passwords to parse
2196 * @error: location to store error, or %NULL
2198 * Parse passwords given in @passwd_file and insert them into a hash table.
2199 * Example of @passwd_file contents:
2200 * wifi.psk:tajne heslo
2201 * 802-1x.password:krakonos
2202 * 802-11-wireless-security:leap-password:my leap password
2204 * Returns: hash table with parsed passwords, or %NULL on an error
2207 parse_passwords (const char *passwd_file, GError **error)
2209 GHashTable *pwds_hash;
2210 char *contents = NULL;
2212 GError *local_err = NULL;
2213 char **lines, **iter;
2214 char *pwd_spec, *pwd, *prop;
2215 const char *setting;
2217 pwds_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
2222 /* Read the passwords file */
2223 if (!g_file_get_contents (passwd_file, &contents, &len, &local_err)) {
2224 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2225 _("failed to read passwd-file '%s': %s"),
2226 passwd_file, local_err->message);
2227 g_error_free (local_err);
2228 g_hash_table_destroy (pwds_hash);
2232 lines = nmc_strsplit_set (contents, "\r\n", -1);
2233 for (iter = lines; *iter; iter++) {
2234 pwd = strchr (*iter, ':');
2236 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2237 _("missing colon in 'password' entry '%s'"), *iter);
2242 prop = strchr (*iter, '.');
2244 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2245 _("missing dot in 'password' entry '%s'"), *iter);
2251 while (g_ascii_isspace (*setting))
2253 /* Accept wifi-sec or wifi instead of cumbersome '802-11-wireless-security' */
2254 if (!strcmp (setting, "wifi-sec") || !strcmp (setting, "wifi"))
2255 setting = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
2256 if (nm_setting_lookup_type (setting) == G_TYPE_INVALID) {
2257 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2258 _("invalid setting name in 'password' entry '%s'"), setting);
2262 pwd_spec = g_strdup_printf ("%s.%s", setting, prop);
2263 g_hash_table_insert (pwds_hash, pwd_spec, g_strdup (pwd));
2272 g_hash_table_destroy (pwds_hash);
2279 nmc_activate_connection (NmCli *nmc,
2280 NMConnection *connection,
2285 GAsyncReadyCallback callback,
2288 ActivateConnectionInfo *info;
2290 GHashTable *pwds_hash;
2291 NMDevice *device = NULL;
2292 const char *spec_object = NULL;
2293 gboolean device_found;
2294 GError *local = NULL;
2296 g_return_val_if_fail (nmc != NULL, FALSE);
2297 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2299 if (connection && (ifname || ap || nsp)) {
2300 device_found = find_device_for_connection (nmc, connection, ifname, ap, nsp, &device, &spec_object, &local);
2302 /* Virtual connection may not have their interfaces created yet */
2303 if (!device_found && !nm_connection_is_virtual (connection)) {
2304 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_CON_ACTIVATION,
2305 "%s", local->message);
2306 g_clear_error (&local);
2309 g_clear_error (&local);
2310 } else if (ifname) {
2311 device = nm_client_get_device_by_iface (nmc->client, ifname);
2313 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND,
2314 _("unknown device '%s'."), ifname);
2317 } else if (!connection) {
2318 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND,
2319 _("neither a valid connection nor device given"));
2323 /* Parse passwords given in passwords file */
2324 pwds_hash = parse_passwords (pwds, &local);
2326 g_propagate_error (error, local);
2330 g_hash_table_destroy (nmc->pwds_hash);
2331 nmc->pwds_hash = pwds_hash;
2333 /* Create secret agent */
2334 nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-connect");
2335 if (nmc->secret_agent) {
2336 g_signal_connect (nmc->secret_agent, "request-secrets", G_CALLBACK (nmc_secrets_requested), nmc);
2338 nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent),
2339 nm_object_get_path (NM_OBJECT (connection)));
2343 info = g_malloc0 (sizeof (ActivateConnectionInfo));
2345 info->device = device;
2347 nm_client_activate_connection_async (nmc->client,
2357 static NMCResultCode
2358 do_connection_up (NmCli *nmc, int argc, char **argv)
2360 NMConnection *connection = NULL;
2361 const char *ifname = NULL;
2362 const char *ap = NULL;
2363 const char *nsp = NULL;
2364 const char *pwds = NULL;
2365 GError *error = NULL;
2366 const char *selector = NULL;
2367 const char *name = NULL;
2371 * Set default timeout for connection activation.
2372 * Activation can take quite a long time, use 90 seconds.
2374 if (nmc->timeout == -1)
2379 line = nmc_readline (PROMPT_CONNECTION);
2380 name = line ? line : "";
2382 } else if (strcmp (*argv, "ifname") != 0) {
2383 if ( strcmp (*argv, "id") == 0
2384 || strcmp (*argv, "uuid") == 0
2385 || strcmp (*argv, "path") == 0) {
2388 if (next_arg (&argc, &argv) != 0) {
2389 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
2390 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2396 next_arg (&argc, &argv);
2400 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
2402 g_string_printf (nmc->return_text, _("Error: Connection '%s' does not exist."), name);
2403 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
2409 if (strcmp (*argv, "ifname") == 0) {
2410 if (next_arg (&argc, &argv) != 0) {
2411 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
2412 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2418 else if (strcmp (*argv, "ap") == 0) {
2419 if (next_arg (&argc, &argv) != 0) {
2420 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
2421 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2427 else if (strcmp (*argv, "passwd-file") == 0) {
2428 if (next_arg (&argc, &argv) != 0) {
2429 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
2430 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2437 g_printerr (_("Unknown parameter: %s\n"), *argv);
2444 /* Use nowait_flag instead of should_wait because exiting has to be postponed till
2445 * active_connection_state_cb() is called. That gives NM time to check our permissions
2446 * and we can follow activation progress.
2448 nmc->nowait_flag = (nmc->timeout == 0);
2451 if (!nmc_activate_connection (nmc, connection, ifname, ap, nsp, pwds, activate_connection_cb, &error)) {
2452 g_string_printf (nmc->return_text, _("Error: %s."),
2454 nmc->return_value = error->code;
2455 g_clear_error (&error);
2460 /* Start progress indication */
2461 if (nmc->print_output == NMC_PRINT_PRETTY)
2462 progress_id = g_timeout_add (120, progress_cb, _("preparing"));
2466 return nmc->return_value;
2475 static void connection_cb_info_finish (ConnectionCbInfo *info,
2476 gpointer connection);
2479 connection_removed_cb (NMClient *client, NMConnection *connection, ConnectionCbInfo *info)
2481 if (!g_slist_find (info->queue, connection))
2483 g_print (_("Connection '%s' (%s) successfully deleted.\n"),
2484 nm_connection_get_id (connection),
2485 nm_connection_get_uuid (connection));
2486 connection_cb_info_finish (info, connection);
2490 down_active_connection_state_cb (NMActiveConnection *active,
2492 ConnectionCbInfo *info)
2494 if (nm_active_connection_get_state (active) < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)
2497 if (info->nmc->print_output == NMC_PRINT_PRETTY)
2498 nmc_terminal_erase_line ();
2499 g_print (_("Connection '%s' successfully deactivated (D-Bus active path: %s)\n"),
2500 nm_active_connection_get_id (active), nm_object_get_path (NM_OBJECT (active)));
2502 g_signal_handlers_disconnect_by_func (G_OBJECT (active),
2503 down_active_connection_state_cb,
2505 connection_cb_info_finish (info, active);
2509 connection_op_timeout_cb (gpointer user_data)
2511 ConnectionCbInfo *info = user_data;
2513 timeout_cb (info->nmc);
2514 connection_cb_info_finish (info, NULL);
2515 return G_SOURCE_REMOVE;
2519 destroy_queue_element (gpointer data)
2521 g_signal_handlers_disconnect_matched (data, G_SIGNAL_MATCH_FUNC, 0, 0, 0,
2522 down_active_connection_state_cb, NULL);
2523 g_object_unref (data);
2527 connection_cb_info_finish (ConnectionCbInfo *info, gpointer connection)
2530 info->queue = g_slist_remove (info->queue, connection);
2531 g_object_unref (G_OBJECT (connection));
2533 g_slist_free_full (info->queue, destroy_queue_element);
2540 if (info->timeout_id)
2541 g_source_remove (info->timeout_id);
2542 g_signal_handlers_disconnect_by_func (info->nmc->client, connection_removed_cb, info);
2543 g_slice_free (ConnectionCbInfo, info);
2547 static NMCResultCode
2548 do_connection_down (NmCli *nmc, int argc, char **argv)
2550 NMActiveConnection *active;
2551 ConnectionCbInfo *info = NULL;
2552 const GPtrArray *active_cons;
2553 GSList *queue = NULL, *iter;
2554 char **arg_arr = NULL;
2555 char **arg_ptr = argv;
2559 if (nmc->timeout == -1)
2564 char *line = nmc_readline (PROMPT_ACTIVE_CONNECTIONS);
2565 nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
2570 g_string_printf (nmc->return_text, _("Error: No connection specified."));
2571 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2576 /* Get active connections */
2577 active_cons = nm_client_get_active_connections (nmc->client);
2578 while (arg_num > 0) {
2579 const char *selector = NULL;
2581 if ( strcmp (*arg_ptr, "id") == 0
2582 || strcmp (*arg_ptr, "uuid") == 0
2583 || strcmp (*arg_ptr, "path") == 0
2584 || strcmp (*arg_ptr, "apath") == 0) {
2586 selector = *arg_ptr;
2587 if (next_arg (&arg_num, &arg_ptr) != 0) {
2588 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
2589 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2594 active = find_active_connection (active_cons, nmc->connections, selector, *arg_ptr, &idx);
2596 /* Check if the connection is unique. */
2597 /* Calling down for the same connection repeatedly would result in
2598 * NM responding for the last D-Bus call only and we would stall. */
2599 if (!g_slist_find (queue, active))
2600 queue = g_slist_prepend (queue, g_object_ref (active));
2602 g_printerr (_("Error: '%s' is not an active connection.\n"), *arg_ptr);
2603 g_string_printf (nmc->return_text, _("Error: not all active connections found."));
2604 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
2608 next_arg (&arg_num, &arg_ptr);
2612 g_string_printf (nmc->return_text, _("Error: no active connection provided."));
2613 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
2616 queue = g_slist_reverse (queue);
2618 if (nmc->timeout > 0) {
2621 info = g_slice_new0 (ConnectionCbInfo);
2623 info->queue = queue;
2624 info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
2627 for (iter = queue; iter; iter = g_slist_next (iter)) {
2628 active = iter->data;
2631 g_signal_connect (active,
2632 "notify::" NM_ACTIVE_CONNECTION_STATE,
2633 G_CALLBACK (down_active_connection_state_cb),
2636 /* Now deactivate the connection */
2637 nm_client_deactivate_connection (nmc->client, active, NULL, NULL);
2641 g_strfreev (arg_arr);
2642 return nmc->return_value;
2645 /*----------------------------------------------------------------------------*/
2647 typedef struct NameItem {
2650 const struct NameItem *settings;
2654 static const NameItem nmc_generic_settings [] = {
2655 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2656 { NULL, NULL, NULL, FALSE }
2659 static const NameItem nmc_ethernet_settings [] = {
2660 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2661 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE },
2662 { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE },
2663 { NM_SETTING_DCB_SETTING_NAME, NULL, NULL, FALSE },
2664 { NULL, NULL, NULL, FALSE }
2667 static const NameItem nmc_infiniband_settings [] = {
2668 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2669 { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, NULL, TRUE },
2670 { NULL, NULL, NULL, FALSE }
2673 static const NameItem nmc_wifi_settings [] = {
2674 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2675 { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", NULL, TRUE },
2676 { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-sec", NULL, FALSE },
2677 { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE },
2678 { NULL, NULL, NULL, FALSE }
2681 static const NameItem nmc_wimax_settings [] = {
2682 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2683 { NM_SETTING_WIMAX_SETTING_NAME, NULL, NULL, TRUE },
2684 { NULL, NULL, NULL, FALSE }
2687 static const NameItem nmc_gsm_settings [] = {
2688 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2689 { NM_SETTING_GSM_SETTING_NAME, NULL, NULL, TRUE },
2690 { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE },
2691 { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE },
2692 { NULL, NULL, NULL, FALSE }
2695 static const NameItem nmc_cdma_settings [] = {
2696 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2697 { NM_SETTING_CDMA_SETTING_NAME, NULL, NULL, TRUE },
2698 { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE },
2699 { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE },
2700 { NULL, NULL, NULL, FALSE }
2703 static const NameItem nmc_bluetooth_settings [] = {
2704 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2705 { NM_SETTING_BLUETOOTH_SETTING_NAME, NULL, NULL, TRUE },
2706 { NULL, NULL, NULL, FALSE }
2709 static const NameItem nmc_adsl_settings [] = {
2710 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2711 { NM_SETTING_ADSL_SETTING_NAME, NULL, NULL, TRUE },
2712 { NULL, NULL, NULL, FALSE }
2715 /* PPPoE is a base connection type from historical reasons.
2716 * See libnm-core/nm-setting.c:_nm_setting_is_base_type()
2718 static const NameItem nmc_pppoe_settings [] = {
2719 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2720 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE },
2721 { NM_SETTING_PPPOE_SETTING_NAME, NULL, NULL, TRUE },
2722 { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE },
2723 { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE },
2724 { NULL, NULL, NULL, FALSE }
2727 static const NameItem nmc_olpc_mesh_settings [] = {
2728 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2729 { NM_SETTING_OLPC_MESH_SETTING_NAME, "olpc-mesh", NULL, TRUE },
2730 { NULL, NULL, NULL, FALSE }
2733 static const NameItem nmc_vpn_settings [] = {
2734 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2735 { NM_SETTING_VPN_SETTING_NAME, NULL, NULL, TRUE },
2736 { NULL, NULL, NULL, FALSE }
2739 static const NameItem nmc_vlan_settings [] = {
2740 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2741 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2742 { NM_SETTING_VLAN_SETTING_NAME, NULL, NULL, TRUE },
2743 { NULL, NULL, NULL, FALSE }
2746 static const NameItem nmc_bond_settings [] = {
2747 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2748 { NM_SETTING_BOND_SETTING_NAME, NULL, NULL, TRUE },
2749 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2750 { NULL, NULL, NULL, FALSE }
2753 static const NameItem nmc_team_settings [] = {
2754 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2755 { NM_SETTING_TEAM_SETTING_NAME, NULL, NULL, TRUE },
2756 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2757 { NULL, NULL, NULL, FALSE }
2760 static const NameItem nmc_bridge_settings [] = {
2761 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2762 { NM_SETTING_BRIDGE_SETTING_NAME, NULL, NULL, TRUE },
2763 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2764 { NULL, NULL, NULL, FALSE }
2767 static const NameItem nmc_bond_slave_settings [] = {
2768 { NULL, NULL, NULL, FALSE }
2771 static const NameItem nmc_team_slave_settings [] = {
2772 { NM_SETTING_TEAM_PORT_SETTING_NAME, NULL, NULL, TRUE },
2773 { NULL, NULL, NULL, FALSE }
2776 static const NameItem nmc_bridge_slave_settings [] = {
2777 { NM_SETTING_BRIDGE_PORT_SETTING_NAME, NULL, NULL, TRUE },
2778 { NULL, NULL, NULL, FALSE }
2781 static const NameItem nmc_no_slave_settings [] = {
2782 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
2783 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
2784 { NULL, NULL, NULL, FALSE }
2787 static const NameItem nmc_tun_settings [] = {
2788 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2789 { NM_SETTING_TUN_SETTING_NAME, NULL, NULL, TRUE },
2790 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2791 { NULL, NULL, NULL, FALSE }
2794 static const NameItem nmc_ip_tunnel_settings [] = {
2795 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2796 { NM_SETTING_IP_TUNNEL_SETTING_NAME, NULL, NULL, TRUE },
2797 { NULL, NULL, NULL, FALSE }
2800 static const NameItem nmc_macvlan_settings [] = {
2801 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2802 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2803 { NM_SETTING_MACVLAN_SETTING_NAME, NULL, NULL, TRUE },
2804 { NULL, NULL, NULL, FALSE }
2807 static const NameItem nmc_vxlan_settings [] = {
2808 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2809 { NM_SETTING_VXLAN_SETTING_NAME, NULL, NULL, TRUE },
2810 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2811 { NULL, NULL, NULL, FALSE }
2814 /* Available connection types */
2815 static const NameItem nmc_valid_connection_types[] = {
2816 { NM_SETTING_GENERIC_SETTING_NAME, NULL, nmc_generic_settings },
2817 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", nmc_ethernet_settings },
2818 { NM_SETTING_PPPOE_SETTING_NAME, NULL, nmc_pppoe_settings },
2819 { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", nmc_wifi_settings },
2820 { NM_SETTING_WIMAX_SETTING_NAME, NULL, nmc_wimax_settings },
2821 { NM_SETTING_GSM_SETTING_NAME, NULL, nmc_gsm_settings },
2822 { NM_SETTING_CDMA_SETTING_NAME, NULL, nmc_cdma_settings },
2823 { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, nmc_infiniband_settings },
2824 { NM_SETTING_ADSL_SETTING_NAME, NULL, nmc_adsl_settings },
2825 { NM_SETTING_BLUETOOTH_SETTING_NAME, NULL, nmc_bluetooth_settings },
2826 { NM_SETTING_VPN_SETTING_NAME, NULL, nmc_vpn_settings },
2827 { NM_SETTING_OLPC_MESH_SETTING_NAME, "olpc-mesh", nmc_olpc_mesh_settings },
2828 { NM_SETTING_VLAN_SETTING_NAME, NULL, nmc_vlan_settings },
2829 { NM_SETTING_BOND_SETTING_NAME, NULL, nmc_bond_settings },
2830 { NM_SETTING_TEAM_SETTING_NAME, NULL, nmc_team_settings },
2831 { NM_SETTING_BRIDGE_SETTING_NAME, NULL, nmc_bridge_settings },
2832 { "bond-slave", NULL, nmc_bond_slave_settings },
2833 { "team-slave", NULL, nmc_team_slave_settings },
2834 { "bridge-slave", NULL, nmc_bridge_slave_settings },
2835 { "no-slave", NULL, nmc_no_slave_settings },
2836 { NM_SETTING_TUN_SETTING_NAME, NULL, nmc_tun_settings },
2837 { NM_SETTING_IP_TUNNEL_SETTING_NAME, NULL, nmc_ip_tunnel_settings },
2838 { NM_SETTING_MACVLAN_SETTING_NAME, NULL, nmc_macvlan_settings },
2839 { NM_SETTING_VXLAN_SETTING_NAME, NULL, nmc_vxlan_settings },
2840 { NULL, NULL, NULL }
2844 * Return an alias for the 'name' if exists, else return the 'name'.
2845 * The returned string must not be freed.
2848 get_name_alias (const char *name, const NameItem array[])
2850 const NameItem *iter = &array[0];
2855 while (iter && iter->name) {
2856 if (!strcmp (name, iter->name)) {
2868 * Construct a string with names and aliases from the arrays formatted as:
2869 * "name (alias), name, name (alias), name, name"
2871 * Returns: string; the caller is responsible for freeing it.
2874 get_valid_options_string (const NameItem *array, const NameItem *array_slv)
2876 const NameItem *iter = array;
2880 str = g_string_sized_new (150);
2882 for (i = 0; i < 2; i++, iter = array_slv) {
2883 while (iter && iter->name) {
2885 g_string_append (str, ", ");
2887 g_string_append_printf (str, "%s (%s)", iter->name, iter->alias);
2889 g_string_append (str, iter->name);
2893 return g_string_free (str, FALSE);
2896 static const NameItem *
2897 get_valid_settings_array (const char *con_type)
2904 num = G_N_ELEMENTS (nmc_valid_connection_types);
2905 for (i = 0; i < num; i++) {
2906 if (nm_streq0 (con_type, nmc_valid_connection_types[i].name))
2907 return nmc_valid_connection_types[i].settings;
2913 * Check if 'val' is valid string in either array->name or array->alias for
2914 * both array parameters (array & array_slv).
2915 * It accepts shorter string provided they are not ambiguous.
2916 * 'val' == NULL doesn't hurt.
2918 * Returns: pointer to array->name string or NULL on failure.
2919 * The returned string must not be freed.
2922 check_valid_name (const char *val, const NameItem *array, const NameItem *array_slv, GError **error)
2924 const NameItem *iter;
2925 gs_unref_ptrarray GPtrArray *tmp_arr = NULL;
2927 GError *tmp_err = NULL;
2930 g_return_val_if_fail (val, NULL);
2931 g_return_val_if_fail (array, NULL);
2933 /* Create a temporary array that can be used in nmc_string_is_valid() */
2934 tmp_arr = g_ptr_array_sized_new (32);
2936 for (i = 0; i < 2; i++, iter = array_slv) {
2937 while (iter && iter->name) {
2938 g_ptr_array_add (tmp_arr, (gpointer) iter->name);
2940 g_ptr_array_add (tmp_arr, (gpointer) iter->alias);
2944 g_ptr_array_add (tmp_arr, (gpointer) NULL);
2946 /* Check string validity */
2947 str = nmc_string_is_valid (val, (const char **) tmp_arr->pdata, &tmp_err);
2949 if (tmp_err->code == 1)
2950 g_propagate_error (error, tmp_err);
2952 /* We want to handle aliases, so construct own error message */
2953 char *err_str = get_valid_options_string (array, array_slv);
2955 g_set_error (error, 1, 0, _("'%s' not among [%s]"),
2958 g_clear_error (&tmp_err);
2963 /* Return a pointer to the found string in passed 'array' */
2965 for (i = 0; i < 2; i++, iter = array_slv) {
2966 while (iter && iter->name) {
2967 if ( nm_streq (iter->name, str)
2968 || nm_streq0 (iter->alias, str)) {
2975 /* We should not really come here */
2976 g_set_error (error, 1, 0, _("Unknown error"));
2981 is_setting_mandatory (NMConnection *connection, NMSetting *setting)
2983 NMSettingConnection *s_con;
2985 const NameItem *item;
2990 s_con = nm_connection_get_setting_connection (connection);
2992 c_type = nm_setting_connection_get_connection_type (s_con);
2994 name = nm_setting_get_name (setting);
2996 item = get_valid_settings_array (c_type);
2997 while (item && item->name) {
2998 if (!strcmp (name, item->name))
2999 return item->mandatory;
3003 /* Let's give a try to parameters related to slave type */
3004 s_type = nm_setting_connection_get_slave_type (s_con);
3005 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
3006 item = get_valid_settings_array (slv_type);
3008 while (item && item->name) {
3009 if (!strcmp (name, item->name))
3010 return item->mandatory;
3017 /*----------------------------------------------------------------------------*/
3020 check_mac (const char *mac,
3022 const char *keyword,
3025 g_return_val_if_fail (type == ARPHRD_ETHER || type == ARPHRD_INFINIBAND, FALSE);
3030 if (!nm_utils_hwaddr_valid (mac, nm_utils_hwaddr_len (type))) {
3031 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3032 _("Error: '%s': '%s' is not a valid %s MAC address."),
3033 keyword, mac, type == ARPHRD_INFINIBAND ? _("InfiniBand") : _("Ethernet"));
3041 check_and_convert_mtu (const char *mtu, guint32 *mtu_int, GError **error)
3043 unsigned long local_mtu_int;
3051 if (!nmc_string_to_uint (mtu, TRUE, 0, G_MAXUINT32, &local_mtu_int)) {
3052 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3053 _("Error: 'mtu': '%s' is not a valid MTU."), mtu);
3057 *mtu_int = (guint32) local_mtu_int;
3062 check_infiniband_parent (const char *parent, GError **error)
3067 if (!nm_utils_iface_valid_name (parent)) {
3068 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3069 _("Error: 'parent': '%s' is not a valid interface name."), parent);
3077 check_infiniband_p_key (const char *p_key, guint32 *p_key_int, GError **error)
3079 unsigned long local_p_key_int;
3080 gboolean p_key_valid = FALSE;
3084 if (!strncmp (p_key, "0x", 2))
3085 p_key_valid = nmc_string_to_uint_base (p_key + 2, 16, TRUE, 0, G_MAXUINT16, &local_p_key_int);
3087 p_key_valid = nmc_string_to_uint (p_key, TRUE, 0, G_MAXUINT16, &local_p_key_int);
3089 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3090 _("Error: 'p-key': '%s' is not a valid InfiniBand P_KEY."), p_key);
3094 *p_key_int = (guint32) local_p_key_int;
3099 check_user_group_id (const char *id, GError **error)
3101 unsigned long int value;
3103 if (!nmc_string_to_uint (id, FALSE, 0, 0, &value)) {
3104 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3105 _("Error: '%s' is not a valid UID/GID."), id);
3113 * check_valid_enumeration:
3114 * @str: string to check against string array @strings
3115 * @strings: string array to check @str againt
3116 * @what: what parameter @str belongs to (used in error message)
3117 * @what_desc: longer description of @what parameter (used in error message)
3118 * @error: location to store an error, or %NULL
3120 * Check whether @str is one of the string of @strings array. It accepts
3121 * shortcuts and normalizes them (@str argument is modified on success).
3123 * Returns: %TRUE on success, %FALSE on failure
3126 check_valid_enumeration (char **str,
3127 const char *strings[],
3129 const char *what_desc,
3133 const char *checked_str;
3138 tmp = g_strstrip (g_strdup (*str));
3139 checked_str = nmc_string_is_valid (tmp, strings, NULL);
3143 *str = g_strdup (checked_str);
3147 options = nmc_util_strv_for_display (strings, TRUE);
3148 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3149 _("Error: '%s': '%s' is not a valid %s %s."),
3150 what, *str, what_desc, options);
3153 return !!checked_str;
3156 /* Checks Wi-Fi mode. */
3158 check_wifi_mode (char **mode, GError **error)
3160 const char *modes[] = { "infrastructure", "ap", "adhoc", NULL };
3162 return check_valid_enumeration (mode, modes, "mode", _("Wi-Fi mode"), error);
3165 /* Checks InfiniBand mode. */
3167 check_infiniband_mode (char **mode, GError **error)
3169 const char *modes[] = { "datagram", "connected", NULL };
3171 return check_valid_enumeration (mode, modes, "mode", _("InfiniBand transport mode"), error);
3174 /* Checks ADSL protocol */
3176 check_adsl_protocol (char **protocol, GError **error)
3178 const char *protos[] = { NM_SETTING_ADSL_PROTOCOL_PPPOA,
3179 NM_SETTING_ADSL_PROTOCOL_PPPOE,
3180 NM_SETTING_ADSL_PROTOCOL_IPOATM,
3183 return check_valid_enumeration (protocol, protos, "protocol", _("ADSL protocol"), error);
3186 /* Checks ADSL encapsulation */
3188 check_adsl_encapsulation (char **encapsulation, GError **error)
3190 const char *modes[] = { NM_SETTING_ADSL_ENCAPSULATION_VCMUX,
3191 NM_SETTING_ADSL_ENCAPSULATION_LLC,
3194 return check_valid_enumeration (encapsulation, modes, "encapsulation", _("ADSL encapsulation"), error);
3197 /* Checks TUN mode. */
3199 check_tun_mode (char **mode, GError **error)
3201 const char *modes[] = { "tun", "tap", NULL };
3203 return check_valid_enumeration (mode, modes, "mode", _("TUN device mode"), error);
3207 check_and_convert_vlan_flags (const char *flags, guint32 *flags_int, GError **error)
3209 unsigned long local_flags_int;
3214 if (!nmc_string_to_uint (flags, TRUE, 0, 7, &local_flags_int)) {
3215 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3216 _("Error: 'flags': '%s' is not valid; use <0-7>."), flags);
3220 *flags_int = (guint32) local_flags_int;
3225 check_and_convert_vlan_prio_maps (const char *prio_map,
3226 NMVlanPriorityMap type,
3227 char ***prio_map_arr,
3230 char **local_prio_map_arr;
3231 GError *local_err = NULL;
3236 if (!(local_prio_map_arr = nmc_vlan_parse_priority_maps (prio_map, type, &local_err))) {
3237 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3238 _("Error: '%s': '%s' is not valid; %s "),
3239 type == NM_VLAN_INGRESS_MAP ? "ingress" : "egress",
3240 prio_map, local_err->message);
3245 *prio_map_arr = local_prio_map_arr;
3250 add_ip4_address_to_connection (NMIPAddress *ip4addr, NMConnection *connection)
3252 NMSettingIPConfig *s_ip4;
3258 s_ip4 = nm_connection_get_setting_ip4_config (connection);
3260 s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
3261 nm_connection_add_setting (connection, NM_SETTING (s_ip4));
3262 g_object_set (s_ip4,
3263 NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
3266 ret = nm_setting_ip_config_add_address (s_ip4, ip4addr);
3267 nm_ip_address_unref (ip4addr);
3273 add_ip6_address_to_connection (NMIPAddress *ip6addr, NMConnection *connection)
3275 NMSettingIPConfig *s_ip6;
3281 s_ip6 = nm_connection_get_setting_ip6_config (connection);
3283 s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
3284 nm_connection_add_setting (connection, NM_SETTING (s_ip6));
3285 g_object_set (s_ip6,
3286 NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
3289 ret = nm_setting_ip_config_add_address (s_ip6, ip6addr);
3290 nm_ip_address_unref (ip6addr);
3296 unique_master_iface_ifname (const GPtrArray *connections,
3297 const char *try_name)
3299 NMConnection *connection;
3301 unsigned int num = 1;
3303 const char *ifname = NULL;
3305 new_name = g_strdup (try_name);
3306 while (i < connections->len) {
3307 connection = NM_CONNECTION (connections->pdata[i]);
3308 ifname = nm_connection_get_interface_name (connection);
3309 if (g_strcmp0 (new_name, ifname) == 0) {
3311 new_name = g_strdup_printf ("%s%d", try_name, num++);
3320 _strip_master_prefix (const char *master, const char *(**func)(NMConnection *))
3325 if (g_str_has_prefix (master, "ifname/")) {
3326 master = master + strlen ("ifname/");
3328 *func = nm_connection_get_interface_name;
3329 } else if (g_str_has_prefix (master, "uuid/")) {
3330 master = master + strlen ("uuid/");
3332 *func = nm_connection_get_uuid;
3333 } else if (g_str_has_prefix (master, "id/")) {
3334 master = master + strlen ("id/");
3336 *func = nm_connection_get_id;
3341 /* normalized_master_for_slave:
3342 * @connections: list af all connections
3343 * @master: UUID, ifname or ID of the master connection
3344 * @type: virtual connection type (bond, team, bridge, ...) or %NULL
3345 * @out_type: type of the connection that matched
3347 * Check whether master is a valid interface name, UUID or ID of some connection,
3348 * possibly of a specified @type.
3349 * First UUID and ifname are checked. If they don't match, ID is checked
3350 * and replaced by UUID on a match.
3352 * Returns: identifier of master connection if found, %NULL otherwise
3355 normalized_master_for_slave (const GPtrArray *connections,
3358 const char **out_type)
3360 NMConnection *connection;
3361 NMSettingConnection *s_con;
3362 const char *con_type = NULL, *id, *uuid, *ifname;
3364 const char *found_by_id = NULL;
3365 const char *out_type_by_id = NULL;
3366 const char *out_master = NULL;
3367 const char *(*func) (NMConnection *) = NULL;
3372 master = _strip_master_prefix (master, &func);
3373 for (i = 0; i < connections->len; i++) {
3374 connection = NM_CONNECTION (connections->pdata[i]);
3375 s_con = nm_connection_get_setting_connection (connection);
3377 con_type = nm_setting_connection_get_connection_type (s_con);
3378 if (type && g_strcmp0 (con_type, type) != 0)
3381 /* There was a prefix; only compare to that type. */
3382 if (g_strcmp0 (master, func (connection)) == 0) {
3384 *out_type = con_type;
3385 if (func == nm_connection_get_id)
3386 out_master = nm_connection_get_uuid (connection);
3388 out_master = master;
3392 id = nm_connection_get_id (connection);
3393 uuid = nm_connection_get_uuid (connection);
3394 ifname = nm_connection_get_interface_name (connection);
3395 if ( g_strcmp0 (master, uuid) == 0
3396 || g_strcmp0 (master, ifname) == 0) {
3397 out_master = master;
3399 *out_type = con_type;
3402 if (!found_by_id && g_strcmp0 (master, id) == 0) {
3403 out_type_by_id = con_type;
3410 out_master = found_by_id;
3412 *out_type = out_type_by_id;
3416 g_print (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master);
3417 out_master = master;
3426 bridge_prop_string_to_uint (const char *str,
3427 const char *nmc_arg,
3429 const char *propname,
3430 unsigned long *out_val,
3433 GParamSpecUInt *pspec;
3435 pspec = (GParamSpecUInt *) g_object_class_find_property (g_type_class_peek (bridge_type),
3437 g_assert (G_IS_PARAM_SPEC_UINT (pspec));
3439 if (!nmc_string_to_uint (str, TRUE, pspec->minimum, pspec->maximum, out_val)) {
3440 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3441 _("Error: '%s': '%s' is not valid; use <%u-%u>."),
3442 nmc_arg, str, pspec->minimum, pspec->maximum);
3448 #define WORD_YES "yes"
3449 #define WORD_NO "no"
3450 #define WORD_LOC_YES _("yes")
3451 #define WORD_LOC_NO _("no")
3453 prompt_yes_no (gboolean default_yes, char *delim)
3455 static char prompt[128] = { 0 };
3460 snprintf (prompt, sizeof (prompt), "(%s/%s) [%s]%s ",
3461 WORD_LOC_YES, WORD_LOC_NO,
3462 default_yes ? WORD_LOC_YES : WORD_LOC_NO, delim);
3468 normalize_yes_no (char **yes_no)
3471 const char *checked_yes_no;
3472 const char *strv[] = { WORD_LOC_YES, WORD_LOC_NO, NULL };
3474 if (!yes_no || !*yes_no)
3477 tmp = g_strstrip (g_strdup (*yes_no));
3478 checked_yes_no = nmc_string_is_valid (tmp, strv, NULL);
3480 if (g_strcmp0 (checked_yes_no, WORD_LOC_YES) == 0) {
3482 *yes_no = g_strdup (WORD_YES);
3483 } else if (g_strcmp0 (checked_yes_no, WORD_LOC_NO) == 0) {
3485 *yes_no = g_strdup (WORD_NO);
3487 return !!checked_yes_no;
3491 want_provide_opt_args (const char *type, int num)
3494 gboolean ret = TRUE;
3496 /* Ask for optional arguments. */
3497 g_print (ngettext ("There is %d optional argument for '%s' connection type.\n",
3498 "There are %d optional arguments for '%s' connection type.\n", num),
3500 answer = nmc_readline (ngettext ("Do you want to provide it? %s",
3501 "Do you want to provide them? %s", num),
3502 prompt_yes_no (TRUE, NULL));
3503 answer = answer ? g_strstrip (answer) : NULL;
3504 if (answer && matches (answer, WORD_LOC_YES) != 0)
3511 do_questionnaire_ethernet (gboolean ethernet, char **mtu, char **mac, char **cloned_mac)
3514 GError *error = NULL;
3516 /* Ask for optional arguments */
3517 if (ethernet && !want_provide_opt_args (_("ethernet"), 3))
3522 *mtu = nmc_readline (_("MTU [auto]: "));
3523 once_more = !check_and_convert_mtu (*mtu, NULL, &error);
3525 g_print ("%s\n", error->message);
3526 g_clear_error (&error);
3529 } while (once_more);
3533 *mac = nmc_readline (_("MAC [none]: "));
3534 once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error);
3536 g_print ("%s\n", error->message);
3537 g_clear_error (&error);
3540 } while (once_more);
3544 *cloned_mac = nmc_readline (_("Cloned MAC [none]: "));
3545 once_more = !check_mac (*cloned_mac, ARPHRD_ETHER, "cloned-mac", &error);
3547 g_print ("%s\n", error->message);
3548 g_clear_error (&error);
3549 g_free (*cloned_mac);
3551 } while (once_more);
3555 #define WORD_DATAGRAM "datagram"
3556 #define WORD_CONNECTED "connected"
3557 #define PROMPT_IB_MODE "(" WORD_DATAGRAM "/" WORD_CONNECTED ") [" WORD_DATAGRAM "]: "
3559 do_questionnaire_infiniband (char **mtu, char **mac, char **mode, char **parent, char **p_key)
3562 GError *error = NULL;
3564 /* Ask for optional arguments */
3565 if (!want_provide_opt_args (_("InfiniBand"), 5))
3570 *mtu = nmc_readline (_("MTU [auto]: "));
3571 once_more = !check_and_convert_mtu (*mtu, NULL, &error);
3573 g_print ("%s\n", error->message);
3574 g_clear_error (&error);
3577 } while (once_more);
3581 *mac = nmc_readline (_("MAC [none]: "));
3582 once_more = !check_mac (*mac, ARPHRD_INFINIBAND, "mac", &error);
3584 g_print ("%s\n", error->message);
3585 g_clear_error (&error);
3588 } while (once_more);
3592 *mode = nmc_readline (_("Transport mode %s"), PROMPT_IB_MODE);
3594 *mode = g_strdup ("datagram");
3595 once_more = !check_infiniband_mode (mode, &error);
3597 g_print ("%s\n", error->message);
3598 g_clear_error (&error);
3601 } while (once_more);
3605 *parent = nmc_readline (_("Parent interface [none]: "));
3606 once_more = !check_infiniband_parent (*parent, &error);
3608 g_print ("%s\n", error->message);
3609 g_clear_error (&error);
3612 } while (once_more);
3616 *p_key = nmc_readline (_("P_KEY [none]: "));
3617 once_more = !check_infiniband_p_key (*p_key, NULL, &error);
3619 g_print ("%s\n", error->message);
3620 g_clear_error (&error);
3623 /* If parent is specified, so has to be P_KEY */
3624 if (!once_more && *parent && !*p_key) {
3626 g_print (_("Error: 'p-key' is mandatory when 'parent' is specified.\n"));
3628 } while (once_more);
3632 #define WORD_INFRA "infrastructure"
3633 #define WORD_AP "ap"
3634 #define WORD_ADHOC "adhoc"
3635 #define PROMPT_WIFI_MODE "(" WORD_INFRA "/" WORD_AP "/" WORD_ADHOC ") [" WORD_INFRA "]: "
3637 do_questionnaire_wifi (char **mtu, char **mac, char **cloned_mac, char **mode)
3640 GError *error = NULL;
3642 /* Ask for optional arguments */
3643 if (!want_provide_opt_args (_("Wi-Fi"), 4))
3646 /* Most optional Wi-Fi arguments are the same as for ethernet. */
3647 do_questionnaire_ethernet (FALSE, mtu, mac, cloned_mac);
3651 *mode = nmc_readline (_("Mode %s"), PROMPT_WIFI_MODE);
3653 *mode = g_strdup ("infrastructure");
3654 once_more = !check_wifi_mode (mode, &error);
3656 g_print ("%s\n", error->message);
3657 g_clear_error (&error);
3660 } while (once_more);
3665 do_questionnaire_wimax (char **mac)
3668 GError *error = NULL;
3670 /* Ask for optional 'wimax' arguments. */
3671 if (!want_provide_opt_args (_("WiMAX"), 1))
3676 *mac = nmc_readline (_("MAC [none]: "));
3677 once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error);
3679 g_print ("%s\n", error->message);
3680 g_clear_error (&error);
3683 } while (once_more);
3688 do_questionnaire_pppoe (gboolean echo, char **password, char **service, char **mtu, char **mac)
3691 GError *error = NULL;
3693 /* Ask for optional 'pppoe' arguments. */
3694 if (!want_provide_opt_args (_("PPPoE"), 4))
3698 *password = nmc_readline_echo (echo, _("Password [none]: "));
3700 *service = nmc_readline (_("Service [none]: "));
3704 *mtu = nmc_readline (_("MTU [auto]: "));
3705 once_more = !check_and_convert_mtu (*mtu, NULL, &error);
3707 g_print ("%s\n", error->message);
3708 g_clear_error (&error);
3711 } while (once_more);
3715 *mac = nmc_readline (_("MAC [none]: "));
3716 once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error);
3718 g_print ("%s\n", error->message);
3719 g_clear_error (&error);
3722 } while (once_more);
3727 do_questionnaire_mobile (gboolean echo, char **user, char **password)
3729 /* Ask for optional 'gsm' or 'cdma' arguments. */
3730 if (!want_provide_opt_args (_("mobile broadband"), 2))
3734 *user = nmc_readline (_("Username [none]: "));
3736 *password = nmc_readline_echo (echo, _("Password [none]: "));
3739 #define WORD_PANU "panu"
3740 #define WORD_DUN_GSM "dun-gsm"
3741 #define WORD_DUN_CDMA "dun-cdma"
3742 #define PROMPT_BT_TYPE "(" WORD_PANU "/" WORD_DUN_GSM "/" WORD_DUN_CDMA ") [" WORD_PANU "]: "
3744 do_questionnaire_bluetooth (char **bt_type)
3748 /* Ask for optional 'bluetooth' arguments. */
3749 if (!want_provide_opt_args (_("bluetooth"), 1))
3753 const char *types[] = { "dun", "dun-gsm", "dun-cdma", "panu", NULL };
3756 *bt_type = nmc_readline (_("Bluetooth type %s"), PROMPT_BT_TYPE);
3758 *bt_type = g_strdup ("panu");
3759 tmp = nmc_string_is_valid (*bt_type, types, NULL);
3762 g_print (_("Error: 'bt-type': '%s' is not a valid bluetooth type.\n"), *bt_type);
3765 } while (once_more);
3767 *bt_type = g_strdup (tmp);
3772 do_questionnaire_vlan (char **mtu, char **flags, char **ingress, char **egress)
3775 GError *error = NULL;
3777 /* Ask for optional 'vlan' arguments. */
3778 if (!want_provide_opt_args (_("VLAN"), 4))
3783 *mtu = nmc_readline (_("MTU [auto]: "));
3784 once_more = !check_and_convert_mtu (*mtu, NULL, &error);
3786 g_print ("%s\n", error->message);
3787 g_clear_error (&error);
3790 } while (once_more);
3794 *flags = nmc_readline (_("VLAN flags (<0-7>) [none]: "));
3795 once_more = !check_and_convert_vlan_flags (*flags, NULL, &error);
3797 g_print ("%s\n", error->message);
3798 g_clear_error (&error);
3801 } while (once_more);
3805 *ingress = nmc_readline (_("Ingress priority maps [none]: "));
3806 once_more = !check_and_convert_vlan_prio_maps (*ingress, NM_VLAN_INGRESS_MAP, NULL, &error);
3808 g_print ("%s\n", error->message);
3809 g_clear_error (&error);
3812 } while (once_more);
3816 *egress = nmc_readline (_("Egress priority maps [none]: "));
3817 once_more = !check_and_convert_vlan_prio_maps (*egress, NM_VLAN_EGRESS_MAP, NULL, &error);
3819 g_print ("%s\n", error->message);
3820 g_clear_error (&error);
3823 } while (once_more);
3827 #define PROMPT_BOND_MODE _("Bonding mode [balance-rr]: ")
3828 #define WORD_MIIMON "miimon"
3829 #define WORD_ARP "arp"
3830 #define PROMPT_BOND_MON_MODE "(" WORD_MIIMON "/" WORD_ARP ") [" WORD_MIIMON "]: "
3832 do_questionnaire_bond (char **mode, char **primary, char **miimon,
3833 char **downdelay, char **updelay,
3834 char **arpinterval, char **arpiptarget,
3840 GError *error = NULL;
3842 /* Ask for optional 'bond' arguments. */
3843 if (!want_provide_opt_args (_("bond"), 5))
3847 const char *mode_tmp;
3849 *mode = nmc_readline (PROMPT_BOND_MODE);
3851 *mode = g_strdup ("balance-rr");
3852 mode_tmp = nmc_bond_validate_mode (*mode, &error);
3855 *mode = g_strdup (mode_tmp);
3857 g_print ("%s\n", error->message);
3858 g_clear_error (&error);
3860 } while (!mode_tmp);
3863 if (g_strcmp0 (*mode, "active-backup") == 0 && !*primary) {
3865 *primary = nmc_readline (_("Bonding primary interface [none]: "));
3866 once_more = *primary && !nm_utils_iface_valid_name (*primary);
3868 g_print (_("Error: 'primary': '%s' is not a valid interface name.\n"),
3872 } while (once_more);
3876 monitor_mode = nmc_readline (_("Bonding monitoring mode %s"), PROMPT_BOND_MON_MODE);
3878 monitor_mode = g_strdup (WORD_MIIMON);
3879 g_strstrip (monitor_mode);
3880 once_more = matches (monitor_mode, WORD_MIIMON) != 0 && matches (monitor_mode, WORD_ARP) != 0;
3882 g_print (_("Error: '%s' is not a valid monitoring mode; use '%s' or '%s'.\n"),
3883 monitor_mode, WORD_MIIMON, WORD_ARP);
3884 g_free (monitor_mode);
3886 } while (once_more);
3888 if (matches (monitor_mode, WORD_MIIMON) == 0) {
3891 *miimon = nmc_readline (_("Bonding miimon [100]: "));
3892 once_more = *miimon && !nmc_string_to_uint (*miimon, TRUE, 0, G_MAXUINT32, &tmp);
3894 g_print (_("Error: 'miimon': '%s' is not a valid number <0-%u>.\n"),
3895 *miimon, G_MAXUINT32);
3898 } while (once_more);
3902 *downdelay = nmc_readline (_("Bonding downdelay [0]: "));
3903 once_more = *downdelay && !nmc_string_to_uint (*downdelay, TRUE, 0, G_MAXUINT32, &tmp);
3905 g_print (_("Error: 'downdelay': '%s' is not a valid number <0-%u>.\n"),
3906 *downdelay, G_MAXUINT32);
3907 g_free (*downdelay);
3909 } while (once_more);
3913 *updelay = nmc_readline (_("Bonding updelay [0]: "));
3914 once_more = *updelay && !nmc_string_to_uint (*updelay, TRUE, 0, G_MAXUINT32, &tmp);
3916 g_print (_("Error: 'updelay': '%s' is not a valid number <0-%u>.\n"),
3917 *updelay, G_MAXUINT32);
3920 } while (once_more);
3923 if (!*arpinterval) {
3925 *arpinterval = nmc_readline (_("Bonding arp-interval [0]: "));
3926 once_more = *arpinterval && !nmc_string_to_uint (*arpinterval, TRUE, 0, G_MAXUINT32, &tmp);
3928 g_print (_("Error: 'arp-interval': '%s' is not a valid number <0-%u>.\n"),
3929 *arpinterval, G_MAXUINT32);
3930 g_free (*arpinterval);
3932 } while (once_more);
3934 if (!*arpiptarget) {
3935 //FIXME: verify the string
3936 *arpiptarget = nmc_readline (_("Bonding arp-ip-target [none]: "));
3941 && (g_strcmp0 (*mode, "802.3ad") == 0 || g_strcmp0 (*mode, "4") == 0)) {
3943 *lacp_rate = nmc_readline (_("LACP rate ('slow' or 'fast') [slow]: "));
3944 once_more = *lacp_rate && (strcmp (*lacp_rate, "slow") &&
3945 strcmp (*lacp_rate, "0") &&
3946 strcmp (*lacp_rate, "fast") &&
3947 strcmp (*lacp_rate, "1"));
3949 printf (_("Error: 'lacp_rate': '%s' is invalid ('slow' or 'fast').\n"),
3951 g_free (*lacp_rate);
3953 } while (once_more);
3956 g_free (monitor_mode);
3960 do_questionnaire_team_common (const char *type_name, char **config)
3964 GError *error = NULL;
3966 /* Ask for optional arguments. */
3967 if (!want_provide_opt_args (type_name, 1))
3972 *config = nmc_readline (_("Team JSON configuration [none]: "));
3973 once_more = !nmc_team_check_config (*config, &json, &error);
3975 g_print ("Error: %s\n", error->message);
3976 g_clear_error (&error);
3979 } while (once_more);
3985 /* Both team and team-slave curently have just ithe same one optional argument */
3987 do_questionnaire_team (char **config)
3989 do_questionnaire_team_common (_("team"), config);
3993 do_questionnaire_team_slave (char **config)
3995 do_questionnaire_team_common (_("team-slave"), config);
3999 do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay, char **hello_time,
4000 char **max_age, char **ageing_time, char **mcast_snoop, char **mac)
4004 GError *error = NULL;
4006 /* Ask for optional 'bridge' arguments. */
4007 if (!want_provide_opt_args (_("bridge"), 8))
4013 *stp = nmc_readline (_("Enable STP %s"), prompt_yes_no (TRUE, ":"));
4014 *stp = *stp ? *stp : g_strdup ("yes");
4015 normalize_yes_no (stp);
4016 once_more = !nmc_string_to_bool (*stp, &stp_bool, &error);
4018 g_print (_("Error: 'stp': %s.\n"), error->message);
4019 g_clear_error (&error);
4022 } while (once_more);
4026 *priority = nmc_readline (_("STP priority [32768]: "));
4027 *priority = *priority ? *priority : g_strdup ("32768");
4028 once_more = !nmc_string_to_uint (*priority, TRUE, 0, G_MAXUINT16, &tmp);
4030 g_print (_("Error: 'priority': '%s' is not a valid number <0-%d>.\n"),
4031 *priority, G_MAXUINT16);
4034 } while (once_more);
4038 *fwd_delay = nmc_readline (_("Forward delay [15]: "));
4039 *fwd_delay = *fwd_delay ? *fwd_delay : g_strdup ("15");
4040 once_more = !nmc_string_to_uint (*fwd_delay, TRUE, 2, 30, &tmp);
4042 g_print (_("Error: 'forward-delay': '%s' is not a valid number <2-30>.\n"),
4044 g_free (*fwd_delay);
4046 } while (once_more);
4051 *hello_time = nmc_readline (_("Hello time [2]: "));
4052 *hello_time = *hello_time ? *hello_time : g_strdup ("2");
4053 once_more = !nmc_string_to_uint (*hello_time, TRUE, 1, 10, &tmp);
4055 g_print (_("Error: 'hello-time': '%s' is not a valid number <1-10>.\n"),
4057 g_free (*hello_time);
4059 } while (once_more);
4063 *max_age = nmc_readline (_("Max age [20]: "));
4064 *max_age = *max_age ? *max_age : g_strdup ("20");
4065 once_more = !nmc_string_to_uint (*max_age, TRUE, 6, 40, &tmp);
4067 g_print (_("Error: 'max-age': '%s' is not a valid number <6-40>.\n"),
4071 } while (once_more);
4073 if (!*ageing_time) {
4075 *ageing_time = nmc_readline (_("MAC address ageing time [300]: "));
4076 *ageing_time = *ageing_time ? *ageing_time : g_strdup ("300");
4077 once_more = !nmc_string_to_uint (*ageing_time, TRUE, 0, 1000000, &tmp);
4079 g_print (_("Error: 'ageing-time': '%s' is not a valid number <0-1000000>.\n"),
4081 g_free (*ageing_time);
4083 } while (once_more);
4085 if (!*mcast_snoop) {
4086 gboolean mcast_snoop_bool;
4088 *mcast_snoop = nmc_readline (_("Enable IGMP snooping %s"), prompt_yes_no (TRUE, ":"));
4089 *mcast_snoop = *mcast_snoop ? *mcast_snoop : g_strdup ("yes");
4090 normalize_yes_no (mcast_snoop);
4091 once_more = !nmc_string_to_bool (*mcast_snoop, &mcast_snoop_bool, &error);
4093 g_print (_("Error: 'multicast-snooping': %s.\n"), error->message);
4094 g_clear_error (&error);
4095 g_free (*mcast_snoop);
4097 } while (once_more);
4101 *mac = nmc_get_user_input (_("MAC [none]: "));
4102 once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error);
4104 g_print ("%s\n", error->message);
4105 g_clear_error (&error);
4108 } while (once_more);
4113 do_questionnaire_bridge_slave (char **priority, char **path_cost, char **hairpin)
4117 GError *error = NULL;
4119 /* Ask for optional 'bridge-slave' arguments. */
4120 if (!want_provide_opt_args (_("bridge-slave"), 3))
4125 *priority = nmc_readline (_("Bridge port priority [32]: "));
4126 *priority = *priority ? *priority : g_strdup ("32");
4127 once_more = !bridge_prop_string_to_uint (*priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
4128 NM_SETTING_BRIDGE_PORT_PRIORITY, &tmp, &error);
4130 g_print ("%s\n", error->message);
4131 g_clear_error (&error);
4134 } while (once_more);
4138 *path_cost = nmc_readline (_("Bridge port STP path cost [100]: "));
4139 *path_cost = *path_cost ? *path_cost : g_strdup ("100");
4140 once_more = !bridge_prop_string_to_uint (*path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
4141 NM_SETTING_BRIDGE_PORT_PATH_COST, &tmp, &error);
4143 g_print ("%s\n", error->message);
4144 g_clear_error (&error);
4145 g_free (*path_cost);
4147 } while (once_more);
4150 gboolean hairpin_bool;
4152 *hairpin = nmc_readline (_("Hairpin %s"), prompt_yes_no (TRUE, ":"));
4153 *hairpin = *hairpin ? *hairpin : g_strdup ("yes");
4154 normalize_yes_no (hairpin);
4155 once_more = !nmc_string_to_bool (*hairpin, &hairpin_bool, &error);
4157 g_print (_("Error: 'hairpin': %s.\n"), error->message);
4158 g_clear_error (&error);
4161 } while (once_more);
4166 do_questionnaire_vpn (char **user)
4168 /* Ask for optional 'vpn' arguments. */
4169 if (!want_provide_opt_args (_("VPN"), 1))
4173 *user = nmc_readline (_("Username [none]: "));
4177 do_questionnaire_olpc (char **channel, char **dhcp_anycast)
4181 GError *error = NULL;
4183 /* Ask for optional 'olpc' arguments. */
4184 if (!want_provide_opt_args (_("OLPC Mesh"), 2))
4189 *channel = nmc_readline (_("OLPC Mesh channel [1]: "));
4190 once_more = *channel && !nmc_string_to_uint (*channel, TRUE, 1, 13, &tmp);
4192 g_print (_("Error: 'channel': '%s' is not a valid number <1-13>.\n"),
4196 } while (once_more);
4198 if (!*dhcp_anycast) {
4200 *dhcp_anycast = nmc_readline (_("DHCP anycast MAC address [none]: "));
4201 once_more = !check_mac (*dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", &error);
4203 g_print ("%s\n", error->message);
4204 g_clear_error (&error);
4205 g_free (*dhcp_anycast);
4207 } while (once_more);
4211 #define PROMPT_ADSL_ENCAP "(" NM_SETTING_ADSL_ENCAPSULATION_VCMUX "/" NM_SETTING_ADSL_ENCAPSULATION_LLC ") [none]: "
4213 do_questionnaire_adsl (gboolean echo, char **password, char **encapsulation)
4216 GError *error = NULL;
4218 /* Ask for optional 'adsl' arguments. */
4219 if (!want_provide_opt_args (_("ADSL"), 2))
4223 *password = nmc_readline_echo (echo, _("Password [none]: "));
4225 if (!*encapsulation) {
4227 *encapsulation = nmc_readline (_("ADSL encapsulation %s"), PROMPT_ADSL_ENCAP);
4228 once_more = !check_adsl_encapsulation (encapsulation, &error);
4230 g_print ("%s\n", error->message);
4231 g_clear_error (&error);
4232 g_free (*encapsulation);
4234 } while (once_more);
4239 do_questionnaire_macvlan (char **tap)
4242 GError *error = NULL;
4244 /* Ask for optional 'macvlan' arguments. */
4245 if (!want_provide_opt_args (_("macvlan"), 1))
4251 *tap = nmc_readline (_("Tap %s"), prompt_yes_no (FALSE, ":"));
4252 *tap = *tap ? *tap : g_strdup ("yes");
4253 normalize_yes_no (tap);
4254 once_more = !nmc_string_to_bool (*tap, &tap_bool, &error);
4256 g_print (_("Error: 'tap': %s.\n"), error->message);
4257 g_clear_error (&error);
4260 } while (once_more);
4265 do_questionnaire_vxlan (char **parent, char **local, char **src_port_min,
4266 char **src_port_max, char **dst_port)
4271 /* Ask for optional 'vxlan' arguments. */
4272 if (!want_provide_opt_args (_("VXLAN"), 5))
4277 *parent = nmc_readline (_("Parent device [none]: "));
4279 && !nm_utils_is_uuid (*parent)
4280 && !nm_utils_iface_valid_name (*parent);
4282 g_print (_("Error: 'dev': '%s' is neither UUID nor interface name.\n"),
4286 } while (once_more);
4291 *local = nmc_readline (_("Local address [none]: "));
4293 && !nm_utils_ipaddr_valid (AF_INET, *local)
4294 && !nm_utils_ipaddr_valid (AF_INET6, *local);
4296 g_print (_("Error: 'local': '%s' is not a valid IP address.\n"),
4300 } while (once_more);
4303 if (!*src_port_min) {
4305 *src_port_min = nmc_readline (_("Minimum source port [0]: "));
4306 *src_port_min = *src_port_min ? *src_port_min : g_strdup ("0");
4307 once_more = !nmc_string_to_uint (*src_port_min, TRUE, 0, 65535, &tmp);
4309 g_print (_("Error: 'source-port-min': '%s' is not a valid number <0-65535>.\n"),
4311 g_free (*src_port_min);
4313 } while (once_more);
4316 if (!*src_port_max) {
4318 *src_port_max = nmc_readline (_("Maximum source port [0]: "));
4319 *src_port_max = *src_port_max ? *src_port_max : g_strdup ("0");
4320 once_more = !nmc_string_to_uint (*src_port_max, TRUE, 0, 65535, &tmp);
4322 g_print (_("Error: 'source-port-max': '%s' is not a valid number <0-65535>.\n"),
4324 g_free (*src_port_max);
4326 } while (once_more);
4331 *dst_port = nmc_readline (_("Destination port [8472]: "));
4332 *dst_port = *dst_port ? *dst_port : g_strdup ("8472");
4333 once_more = !nmc_string_to_uint (*dst_port, TRUE, 0, 65535, &tmp);
4335 g_print (_("Error: 'destination-port': '%s' is not a valid number <0-65535>.\n"),
4339 } while (once_more);
4344 split_address (char* str, char **ip, char **rest)
4352 n1 = strspn (str, " \t");
4353 n2 = strcspn (str+n1, " \t\0") + n1;
4354 n3 = strspn (str+n2, " \t") + n2;
4357 *ip = str[n1] ? str + n1 : NULL;
4358 *rest = str[n3] ? str + n3 : NULL;
4364 ask_for_ip_addresses (NMConnection *connection, int family)
4367 GError *error = NULL;
4368 char *str, *ip, *rest;
4371 NMIPAddress *ipaddr;
4373 if (family == AF_INET)
4374 prompt =_("IPv4 address (IP[/plen]) [none]: ");
4376 prompt =_("IPv6 address (IP[/plen]) [none]: ");
4380 str = nmc_readline ("%s", prompt);
4381 split_address (str, &ip, &rest);
4383 ipaddr = nmc_parse_and_build_address (family, ip, &error);
4385 if (family == AF_INET)
4386 added = add_ip4_address_to_connection (ipaddr, connection);
4388 added = add_ip6_address_to_connection (ipaddr, connection);
4390 g_print (_(" Address successfully added: %s\n"), ip);
4392 g_print (_(" Warning: address already present: %s\n"), ip);
4394 g_print (_(" Warning: ignoring garbage at the end: '%s'\n"), rest);
4396 g_prefix_error (&error, _("Error: "));
4397 g_print ("%s\n", error->message);
4398 g_clear_error (&error);
4408 maybe_ask_for_gateway (NMConnection *connection, int family)
4411 char *str, *gw, *rest;
4413 NMSettingIPConfig *s_ip;
4415 if (family == AF_INET) {
4416 prompt =_("IPv4 gateway [none]: ");
4417 s_ip = nm_connection_get_setting_ip4_config (connection);
4419 prompt =_("IPv6 gateway [none]: ");
4420 s_ip = nm_connection_get_setting_ip6_config (connection);
4424 if ( nm_setting_ip_config_get_num_addresses (s_ip) == 0
4425 || nm_setting_ip_config_get_gateway (s_ip) != NULL)
4430 str = nmc_readline ("%s", prompt);
4431 split_address (str, &gw, &rest);
4433 if (nm_utils_ipaddr_valid (family, gw)) {
4435 NM_SETTING_IP_CONFIG_GATEWAY, gw,
4439 g_print (_("Error: invalid gateway address '%s'\n"), gw);
4447 do_questionnaire_ip (NMConnection *connection)
4451 /* Ask for IP addresses */
4452 answer = nmc_readline (_("Do you want to add IP addresses? %s"), prompt_yes_no (TRUE, NULL));
4453 answer = answer ? g_strstrip (answer) : NULL;
4454 if (answer && matches (answer, WORD_LOC_YES) != 0) {
4460 g_print (_("Press <Enter> to finish adding addresses.\n"));
4462 ask_for_ip_addresses (connection, AF_INET);
4463 maybe_ask_for_gateway (connection, AF_INET);
4464 ask_for_ip_addresses (connection, AF_INET6);
4465 maybe_ask_for_gateway (connection, AF_INET6);
4469 is_setting_valid (NMConnection *connection, const NameItem *valid_settings_main, const NameItem *valid_settings_slave, char *setting)
4471 const char *setting_name;
4473 if (!(setting_name = check_valid_name (setting, valid_settings_main, valid_settings_slave, NULL)))
4475 return nm_connection_get_setting_by_name (connection, setting_name);
4479 is_property_valid (NMSetting *setting, const char *property, GError **error)
4481 char **valid_props = NULL;
4482 const char *prop_name;
4485 valid_props = nmc_setting_get_valid_properties (setting);
4486 prop_name = nmc_string_is_valid (property, (const char **) valid_props, error);
4487 ret = g_strdup (prop_name);
4488 g_strfreev (valid_props);
4492 #define WORD_TUN "tun"
4493 #define WORD_TAP "tap"
4494 #define PROMPT_TUN_MODE "(" WORD_TUN "/" WORD_TAP ") [" WORD_TUN "]: "
4496 do_questionnaire_tun (char **user, char **group,
4497 char **pi, char **vnet_hdr, char **multi_queue)
4500 GError *error = NULL;
4503 /* Ask for optional 'tun' arguments. */
4504 if (!want_provide_opt_args (_("Tun"), 5))
4509 *user = nmc_readline (_("User ID [none]: "));
4512 once_more = !check_user_group_id (*user, &error);
4514 g_print ("%s\n", error->message);
4515 g_clear_error (&error);
4518 } while (once_more);
4522 *group = nmc_readline (_("Group ID [none]: "));
4525 once_more = !check_user_group_id (*group, &error);
4527 g_print ("%s\n", error->message);
4528 g_clear_error (&error);
4531 } while (once_more);
4536 *pi = nmc_readline (_("Enable PI %s"), prompt_yes_no (FALSE, ":"));
4537 *pi = *pi ? *pi : g_strdup ("no");
4538 normalize_yes_no (pi);
4539 once_more = !nmc_string_to_bool (*pi, &b, &error);
4541 g_print (_("Error: 'pi': %s.\n"), error->message);
4542 g_clear_error (&error);
4545 } while (once_more);
4549 *vnet_hdr = nmc_readline (_("Enable VNET header %s"), prompt_yes_no (FALSE, ":"));
4550 *vnet_hdr = *vnet_hdr ? *vnet_hdr : g_strdup ("no");
4551 normalize_yes_no (vnet_hdr);
4552 once_more = !nmc_string_to_bool (*vnet_hdr, &b, &error);
4554 g_print (_("Error: 'vnet-hdr': %s.\n"), error->message);
4555 g_clear_error (&error);
4558 } while (once_more);
4560 if (!*multi_queue) {
4562 *multi_queue = nmc_readline (_("Enable multi queue %s"), prompt_yes_no (FALSE, ":"));
4563 *multi_queue = *multi_queue ? *multi_queue : g_strdup ("no");
4564 normalize_yes_no (multi_queue);
4565 once_more = !nmc_string_to_bool (*multi_queue, &b, &error);
4567 g_print (_("Error: 'multi-queue': %s.\n"), error->message);
4568 g_clear_error (&error);
4569 g_free (*multi_queue);
4571 } while (once_more);
4576 do_questionnaire_ip_tunnel (char **local, char **parent)
4580 /* Ask for optional 'ip-tunnel' arguments. */
4581 if (!want_provide_opt_args (_("IP Tunnel"), 2))
4586 *local = nmc_readline (_("Local endpoint [none]: "));
4589 once_more = !nm_utils_ipaddr_valid (AF_INET, *local)
4590 && !nm_utils_ipaddr_valid (AF_INET6, *local);
4592 g_print (_("Error: 'local': '%s' is not valid; must be an IP address\n"),
4596 } while (once_more);
4601 *parent = nmc_readline (_("Parent device [none]: "));
4603 && !nm_utils_is_uuid (*parent)
4604 && !nm_utils_iface_valid_name (*parent);
4606 g_print (_("Error: 'dev': '%s' is neither UUID nor interface name.\n"),
4610 } while (once_more);
4615 read_connection_properties (NMConnection *connection,
4621 NMSettingConnection *s_con;
4622 const char *con_type;
4623 const char *s_dot_p;
4626 char *slv_type = NULL;
4627 const char *setting_name;
4628 gboolean append = FALSE;
4629 gboolean remove = FALSE;
4630 gboolean success = FALSE;
4631 GError *local = NULL;
4633 s_con = nm_connection_get_setting_connection (connection);
4636 /* First check if we have a slave-type, as this would mean we will not
4637 * have ip properties but possibly others, slave-type specific.
4639 con_type = nm_setting_connection_get_slave_type (s_con);
4643 slv_type = g_strdup_printf ("%s-slave", con_type);
4645 con_type = nm_setting_connection_get_connection_type (s_con);
4647 /* Go through arguments and set properties */
4649 gs_free char *property_name = NULL;
4652 next_arg (&argc, &argv);
4654 next_arg (&argc, &argv);
4657 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4658 _("Error: <setting>.<property> argument is missing."));
4662 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4663 _("Error: value for '%s' is missing."), s_dot_p);
4666 /* Empty string will reset the value to default */
4667 if (value[0] == '\0')
4670 if (s_dot_p[0] == '+') {
4673 } else if (s_dot_p[0] == '-') {
4678 strv = g_strsplit (s_dot_p, ".", 2);
4679 if (g_strv_length (strv) != 2) {
4680 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4681 _("Error: invalid <setting>.<property> '%s'."), s_dot_p);
4685 setting_name = check_valid_name (strv[0], get_valid_settings_array (con_type),
4686 get_valid_settings_array (slv_type), &local);
4687 if (!setting_name) {
4688 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4689 _("Error: invalid or not allowed setting '%s': %s."),
4690 strv[0], local->message);
4691 g_clear_error (&local);
4694 setting = nm_connection_get_setting_by_name (connection, setting_name);
4696 setting = nmc_setting_new_for_name (setting_name);
4698 /* This should really not happen */
4699 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_UNKNOWN,
4700 _("Error: don't know how to create '%s' setting."),
4704 nm_connection_add_setting (connection, setting);
4707 property_name = is_property_valid (setting, strv[1], &local);
4708 if (!property_name) {
4709 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4710 _("Error: invalid property '%s': %s."),
4711 strv[1], local->message);
4712 g_clear_error (&local);
4719 nmc_setting_reset_property (setting, property_name, NULL);
4720 if (!nmc_setting_set_property (setting, property_name, value, &local)) {
4721 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4722 _("Error: failed to modify %s.%s: %s."),
4723 strv[0], strv[1], local->message);
4724 g_clear_error (&local);
4729 * - either empty: remove whole value
4730 * - or specified by index <0-n>: remove item at the index
4731 * - or option name: remove item with the option name
4735 if (nmc_string_to_uint (value, TRUE, 0, G_MAXUINT32, &idx))
4736 nmc_setting_remove_property_option (setting, property_name, NULL, idx, &local);
4738 nmc_setting_remove_property_option (setting, property_name, value, 0, &local);
4740 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4741 _("Error: failed to remove a value from %s.%s: %s."),
4742 strv[0], strv[1], local->message);
4743 g_clear_error (&local);
4747 nmc_setting_reset_property (setting, property_name, NULL);
4763 complete_slave (NMSettingConnection *s_con,
4764 const GPtrArray *all_connections,
4765 const char *slave_type,
4771 char *master_ask = NULL;
4772 const char *checked_master = NULL;
4775 g_print (_("Warning: 'type' is ignored. "
4776 "Use 'nmcli connection add \"%s\" ...' instead."),
4779 if (nm_setting_connection_get_master (s_con)) {
4780 /* Master already set. */
4782 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4783 _("Error: redundant 'master' option."));
4790 master = master_ask = nmc_readline (PROMPT_MASTER);
4792 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4793 _("Error: 'master' is required."));
4796 /* Verify master argument */
4797 checked_master = normalized_master_for_slave (all_connections, master, slave_type, NULL);
4799 /* Change properties in 'connection' setting */
4800 g_object_set (s_con,
4801 NM_SETTING_CONNECTION_MASTER, checked_master,
4802 NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
4805 g_free (master_ask);
4811 complete_connection_by_type (NMConnection *connection,
4812 const char *con_type,
4813 const GPtrArray *all_connections,
4815 gboolean show_secrets,
4820 NMSettingConnection *s_con;
4821 NMSettingGeneric *s_generic;
4822 NMSettingWired *s_wired;
4823 NMSettingInfiniband *s_infiniband;
4824 NMSettingWireless *s_wifi;
4825 NMSettingWimax *s_wimax;
4826 NMSettingPppoe *s_pppoe;
4827 NMSettingGsm *s_gsm;
4828 NMSettingCdma *s_cdma;
4829 NMSettingBluetooth *s_bt;
4830 NMSettingVlan *s_vlan;
4831 NMSettingBond *s_bond;
4832 NMSettingTeam *s_team;
4833 NMSettingTeamPort *s_team_port;
4834 NMSettingBridge *s_bridge;
4835 NMSettingBridgePort *s_bridge_port;
4836 NMSettingVpn *s_vpn;
4837 NMSettingOlpcMesh *s_olpc_mesh;
4838 NMSettingAdsl *s_adsl;
4839 NMSettingTun *s_tun;
4840 NMSettingIPTunnel *s_ip_tunnel;
4841 NMSettingMacvlan *s_macvlan;
4842 NMSettingVxlan *s_vxlan;
4844 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4846 s_con = nm_connection_get_setting_connection (connection);
4849 if (!strcmp (con_type, NM_SETTING_WIRED_SETTING_NAME)) {
4850 /* Build up the settings required for 'ethernet' */
4851 gboolean success = FALSE;
4852 const char *mtu_c = NULL;
4854 guint32 mtu_int = 0;
4855 const char *mac_c = NULL;
4857 const char *cloned_mac_c = NULL;
4858 char *cloned_mac = NULL;
4859 nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE},
4860 {"mac", TRUE, &mac_c, FALSE},
4861 {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
4864 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4867 /* Also ask for all optional arguments if '--ask' is specified. */
4868 mtu = g_strdup (mtu_c);
4869 mac = g_strdup (mac_c);
4870 cloned_mac = g_strdup (cloned_mac_c);
4872 do_questionnaire_ethernet (TRUE, &mtu, &mac, &cloned_mac);
4874 if (!check_and_convert_mtu (mtu, &mtu_int, error))
4876 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
4878 if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error))
4881 /* Add ethernet setting */
4882 s_wired = (NMSettingWired *) nm_setting_wired_new ();
4883 nm_connection_add_setting (connection, NM_SETTING (s_wired));
4886 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
4888 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
4890 g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_mac, NULL);
4896 g_free (cloned_mac);
4900 } else if (!strcmp (con_type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
4901 /* Build up the settings required for 'infiniband' */
4902 gboolean success = FALSE;
4903 const char *mtu_c = NULL;
4905 guint32 mtu_int = 0;
4906 const char *mac_c = NULL;
4908 const char *mode_c = NULL;
4910 const char *parent_c = NULL;
4911 char *parent = NULL;
4912 const char *p_key_c = NULL;
4914 guint32 p_key_int = 0;
4915 nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE},
4916 {"mac", TRUE, &mac_c, FALSE},
4917 {"transport-mode", TRUE, &mode_c, FALSE},
4918 {"parent", TRUE, &parent_c, FALSE},
4919 {"p-key", TRUE, &p_key_c, FALSE},
4922 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4925 /* Also ask for all optional arguments if '--ask' is specified. */
4926 mtu = g_strdup (mtu_c);
4927 mac = g_strdup (mac_c);
4928 mode = g_strdup (mode_c);
4929 parent = g_strdup (parent_c);
4930 p_key = g_strdup (p_key_c);
4932 do_questionnaire_infiniband (&mtu, &mac, &mode, &parent, &p_key);
4934 if (!check_and_convert_mtu (mtu, &mtu_int, error))
4936 if (!check_mac (mac, ARPHRD_INFINIBAND, "mac", error))
4938 if (!check_infiniband_mode (&mode, error))
4941 if (!check_infiniband_p_key (p_key, &p_key_int, error))
4943 if (!check_infiniband_parent (parent, error))
4945 } else if (parent) {
4946 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4947 _("Error: 'parent': not valid without 'p-key'."));
4951 /* Add 'infiniband' setting */
4952 s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new ();
4953 nm_connection_add_setting (connection, NM_SETTING (s_infiniband));
4955 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, mode ? mode : "datagram", NULL);
4957 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu_int, NULL);
4959 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL);
4961 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_P_KEY, p_key_int, NULL);
4963 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_PARENT, parent, NULL);
4976 } else if (!strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME)) {
4977 /* Build up the settings required for 'wifi' */
4978 gboolean success = FALSE;
4979 char *ssid_ask = NULL;
4980 const char *ssid = NULL;
4982 const char *mtu_c = NULL;
4984 guint32 mtu_int = 0;
4985 const char *mac_c = NULL;
4987 const char *cloned_mac_c = NULL;
4988 char *cloned_mac = NULL;
4989 const char *mode_c = NULL;
4991 nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask},
4992 {"mtu", TRUE, &mtu_c, FALSE},
4993 {"mac", TRUE, &mac_c, FALSE},
4994 {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
4995 {"mode", TRUE, &mode_c, FALSE},
4998 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5002 ssid = ssid_ask = nmc_readline (_("SSID: "));
5004 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5005 _("Error: 'ssid' is required."));
5009 /* Also ask for all optional arguments if '--ask' is specified. */
5010 mtu = g_strdup (mtu_c);
5011 mac = g_strdup (mac_c);
5012 cloned_mac = g_strdup (cloned_mac_c);
5013 mode = g_strdup (mode_c);
5015 do_questionnaire_wifi (&mtu, &mac, &cloned_mac, &mode);
5017 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5019 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5021 if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error))
5023 if (!check_wifi_mode (&mode, error))
5026 /* Add wifi setting */
5027 s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
5028 nm_connection_add_setting (connection, NM_SETTING (s_wifi));
5030 ssid_bytes = g_bytes_new (ssid, strlen (ssid));
5031 g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid_bytes, NULL);
5034 g_object_set (s_wifi, NM_SETTING_WIRELESS_MTU, mtu_int, NULL);
5036 g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL);
5038 g_object_set (s_wifi, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, cloned_mac, NULL);
5040 g_object_set (s_wifi, NM_SETTING_WIRELESS_MODE, mode, NULL);
5042 g_bytes_unref (ssid_bytes);
5049 g_free (cloned_mac);
5054 } else if (!strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)) {
5055 /* Build up the settings required for 'wimax' */
5056 gboolean success = FALSE;
5057 const char *nsp_name = NULL;
5058 char *nsp_name_ask = NULL;
5059 const char *mac_c = NULL;
5061 nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !ask},
5062 {"mac", TRUE, &mac_c, FALSE},
5065 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5068 if (!nsp_name && ask)
5069 nsp_name = nsp_name_ask = nmc_readline (_("WiMAX NSP name: "));
5071 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5072 _("Error: 'nsp' is required."));
5076 /* Also ask for all optional arguments if '--ask' is specified. */
5077 mac = g_strdup (mac_c);
5079 do_questionnaire_wimax (&mac);
5081 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5084 /* Add 'wimax' setting */
5085 s_wimax = (NMSettingWimax *) nm_setting_wimax_new ();
5086 nm_connection_add_setting (connection, NM_SETTING (s_wimax));
5087 g_object_set (s_wimax, NM_SETTING_WIMAX_NETWORK_NAME, nsp_name, NULL);
5090 g_object_set (s_wimax, NM_SETTING_WIMAX_MAC_ADDRESS, mac, NULL);
5094 g_free (nsp_name_ask);
5099 } else if (!strcmp (con_type, NM_SETTING_PPPOE_SETTING_NAME)) {
5100 /* Build up the settings required for 'pppoe' */
5101 gboolean success = FALSE;
5102 const char *username = NULL;
5103 char *username_ask = NULL;
5104 const char *password_c = NULL;
5105 char *password = NULL;
5106 const char *service_c = NULL;
5107 char *service = NULL;
5108 const char *mtu_c = NULL;
5110 guint32 mtu_int = 0;
5111 const char *mac_c = NULL;
5113 nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask},
5114 {"password", TRUE, &password_c, FALSE},
5115 {"service", TRUE, &service_c, FALSE},
5116 {"mtu", TRUE, &mtu_c, FALSE},
5117 {"mac", TRUE, &mac_c, FALSE},
5120 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5123 if (!username && ask)
5124 username = username_ask = nmc_readline (_("PPPoE username: "));
5126 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5127 _("Error: 'username' is required."));
5131 /* Also ask for all optional arguments if '--ask' is specified. */
5132 password = g_strdup (password_c);
5133 service = g_strdup (service_c);
5134 mtu = g_strdup (mtu_c);
5135 mac = g_strdup (mac_c);
5137 do_questionnaire_pppoe (show_secrets, &password, &service, &mtu, &mac);
5139 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5141 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5144 /* Add 'pppoe' setting */
5145 s_pppoe = (NMSettingPppoe *) nm_setting_pppoe_new ();
5146 nm_connection_add_setting (connection, NM_SETTING (s_pppoe));
5147 g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, username, NULL);
5148 g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, password, NULL);
5149 g_object_set (s_pppoe, NM_SETTING_PPPOE_SERVICE, service, NULL);
5151 /* Add ethernet setting */
5152 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5153 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5155 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
5157 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
5161 g_free (username_ask);
5169 } else if ( !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME)
5170 || !strcmp (con_type, NM_SETTING_CDMA_SETTING_NAME)) {
5171 /* Build up the settings required for 'gsm' or 'cdma' mobile broadband */
5172 gboolean success = FALSE;
5173 const char *apn = NULL;
5174 char *apn_ask = NULL;
5175 const char *user_c = NULL;
5177 const char *password_c = NULL;
5178 char *password = NULL;
5181 nmc_arg_t gsm_args[] = { {NULL}, {NULL}, {NULL}, /* placeholders */
5184 is_gsm = !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME);
5187 gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !ask};
5188 gsm_args[i++] = (nmc_arg_t) {"user", TRUE, &user_c, FALSE};
5189 gsm_args[i++] = (nmc_arg_t) {"password", TRUE, &password_c, FALSE};
5190 gsm_args[i++] = (nmc_arg_t) {NULL};
5192 if (!nmc_parse_args (gsm_args, FALSE, &argc, &argv, error))
5195 if (!apn && ask && is_gsm)
5196 apn = apn_ask = nmc_readline (_("APN: "));
5197 if (!apn && is_gsm) {
5198 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5199 _("Error: 'apn' is required."));
5200 goto cleanup_mobile;
5203 /* Also ask for all optional arguments if '--ask' is specified. */
5204 user = g_strdup (user_c);
5205 password = g_strdup (password_c);
5207 do_questionnaire_mobile (show_secrets, &user, &password);
5210 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, NULL);
5212 /* Add 'gsm' setting */
5213 s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
5214 nm_connection_add_setting (connection, NM_SETTING (s_gsm));
5215 g_object_set (s_gsm,
5216 NM_SETTING_GSM_NUMBER, "*99#",
5217 NM_SETTING_GSM_APN, apn,
5218 NM_SETTING_GSM_USERNAME, user,
5219 NM_SETTING_GSM_PASSWORD, password,
5223 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME, NULL);
5225 /* Add 'cdma' setting */
5226 s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
5227 nm_connection_add_setting (connection, NM_SETTING (s_cdma));
5228 g_object_set (s_cdma,
5229 NM_SETTING_CDMA_NUMBER, "#777",
5230 NM_SETTING_CDMA_USERNAME, user,
5231 NM_SETTING_CDMA_PASSWORD, password,
5242 } else if (!strcmp (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
5243 /* Build up the settings required for 'bluetooth' */
5244 gboolean success = FALSE;
5245 const char *addr = NULL;
5246 char *addr_ask = NULL;
5247 const char *bt_type_c = NULL;
5248 char *bt_type = NULL;
5249 nmc_arg_t exp_args[] = { {"addr", TRUE, &addr, !ask},
5250 {"bt-type", TRUE, &bt_type_c, FALSE},
5253 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5257 addr = addr_ask = nmc_readline (_("Bluetooth device address: "));
5259 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5260 _("Error: 'addr' is required."));
5263 if (!check_mac (addr, ARPHRD_ETHER, "addr", error))
5266 /* Also ask for all optional arguments if '--ask' is specified. */
5267 bt_type = g_strdup (bt_type_c);
5269 do_questionnaire_bluetooth (&bt_type);
5271 /* Default to 'panu' if bt-type is not provided. */
5273 bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_PANU);
5275 /* Add 'bluetooth' setting */
5276 s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new ();
5277 nm_connection_add_setting (connection, NM_SETTING (s_bt));
5280 g_object_set (s_bt, NM_SETTING_BLUETOOTH_BDADDR, addr, NULL);
5282 /* 'dun' type requires adding 'gsm' or 'cdma' setting */
5283 if ( !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
5284 || !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) {
5285 bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
5286 s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
5287 nm_connection_add_setting (connection, NM_SETTING (s_gsm));
5288 g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
5289 // g_object_set (s_gsm, NM_SETTING_GSM_APN, "FIXME", NULL;
5291 } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) {
5292 bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
5293 s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
5294 nm_connection_add_setting (connection, NM_SETTING (s_cdma));
5295 g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL);
5297 } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) {
5300 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5301 _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."),
5302 bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN,
5303 NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma");
5306 g_object_set (s_bt, NM_SETTING_BLUETOOTH_TYPE, bt_type, NULL);
5315 } else if (!strcmp (con_type, NM_SETTING_VLAN_SETTING_NAME)) {
5316 /* Build up the settings required for 'vlan' */
5317 gboolean success = FALSE;
5318 const char *parent = NULL;
5319 char *parent_ask = NULL;
5320 const char *vlan_id = NULL;
5321 char *vlan_id_ask = NULL;
5322 unsigned long id = 0;
5323 const char *flags_c = NULL;
5325 guint32 flags_int = 0;
5326 const char *ingress_c = NULL, *egress_c = NULL;
5327 char *ingress = NULL, *egress = NULL;
5328 char **ingress_arr = NULL, **egress_arr = NULL, **p;
5329 const char *mtu_c = NULL;
5332 gboolean valid_mac = FALSE;
5333 nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask},
5334 {"id", TRUE, &vlan_id, !ask},
5335 {"flags", TRUE, &flags_c, FALSE},
5336 {"ingress", TRUE, &ingress_c, FALSE},
5337 {"egress", TRUE, &egress_c, FALSE},
5338 {"mtu", TRUE, &mtu_c, FALSE},
5341 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5345 parent = parent_ask = nmc_readline (_("VLAN parent device or connection UUID: "));
5347 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5348 _("Error: 'dev' is required."));
5351 if (!vlan_id && ask)
5352 vlan_id = vlan_id_ask = nmc_readline (_("VLAN ID <0-4094>: "));
5354 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5355 _("Error: 'id' is required."));
5359 if (!nmc_string_to_uint (vlan_id, TRUE, 0, 4094, &id)) {
5360 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5361 _("Error: 'id': '%s' is not valid; use <0-4094>."),
5367 if ( !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN))
5368 && !nm_utils_is_uuid (parent)
5369 && !nm_utils_iface_valid_name (parent)) {
5370 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5371 _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
5376 /* Also ask for all optional arguments if '--ask' is specified. */
5377 mtu = g_strdup (mtu_c);
5378 flags = g_strdup (flags_c);
5379 ingress = g_strdup (ingress_c);
5380 egress = g_strdup (egress_c);
5382 do_questionnaire_vlan (&mtu, &flags, &ingress, &egress);
5384 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5386 if (!check_and_convert_vlan_flags (flags, &flags_int, error))
5388 if (!check_and_convert_vlan_prio_maps (ingress, NM_VLAN_INGRESS_MAP, &ingress_arr, error))
5390 if (!check_and_convert_vlan_prio_maps (egress, NM_VLAN_EGRESS_MAP, &egress_arr, error))
5393 /* Add 'vlan' setting */
5394 s_vlan = (NMSettingVlan *) nm_setting_vlan_new ();
5395 nm_connection_add_setting (connection, NM_SETTING (s_vlan));
5397 /* Add 'wired' setting if necessary */
5398 if (mtu || valid_mac) {
5399 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5400 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5403 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
5405 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL);
5408 /* Set 'vlan' properties */
5410 g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL);
5412 g_object_set (s_vlan, NM_SETTING_VLAN_ID, id, NULL);
5415 g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, flags_int, NULL);
5416 for (p = ingress_arr; p && *p; p++)
5417 nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, *p);
5418 for (p = egress_arr; p && *p; p++)
5419 nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, *p);
5427 g_free (parent_ask);
5428 g_free (vlan_id_ask);
5429 g_strfreev (ingress_arr);
5430 g_strfreev (egress_arr);
5434 } else if (!strcmp (con_type, NM_SETTING_BOND_SETTING_NAME)) {
5435 /* Build up the settings required for 'bond' */
5436 gboolean success = FALSE;
5437 const char *ifname = NULL;
5438 const char *bond_mode_c = NULL;
5439 char *bond_mode = NULL;
5440 const char *bond_primary_c = NULL;
5441 char *bond_primary = NULL;
5442 const char *bond_miimon_c = NULL;
5443 char *bond_miimon = NULL;
5444 const char *bond_downdelay_c = NULL;
5445 char *bond_downdelay = NULL;
5446 const char *bond_updelay_c = NULL;
5447 char *bond_updelay = NULL;
5448 const char *bond_arpinterval_c = NULL;
5449 char *bond_arpinterval = NULL;
5450 const char *bond_arpiptarget_c = NULL;
5451 char *bond_arpiptarget = NULL;
5452 const char *bond_lacp_rate_c = NULL;
5453 char *bond_lacp_rate = NULL;
5454 nmc_arg_t exp_args[] = { {"mode", TRUE, &bond_mode_c, FALSE},
5455 {"primary", TRUE, &bond_primary_c, FALSE},
5456 {"miimon", TRUE, &bond_miimon_c, FALSE},
5457 {"downdelay", TRUE, &bond_downdelay_c, FALSE},
5458 {"updelay", TRUE, &bond_updelay_c, FALSE},
5459 {"arp-interval", TRUE, &bond_arpinterval_c, FALSE},
5460 {"arp-ip-target", TRUE, &bond_arpiptarget_c, FALSE},
5461 {"lacp-rate", TRUE, &bond_lacp_rate_c, FALSE},
5464 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5467 /* Also ask for all optional arguments if '--ask' is specified. */
5468 bond_mode = g_strdup (bond_mode_c);
5469 bond_primary = g_strdup (bond_primary_c);
5470 bond_miimon = g_strdup (bond_miimon_c);
5471 bond_downdelay = g_strdup (bond_downdelay_c);
5472 bond_updelay = g_strdup (bond_updelay_c);
5473 bond_arpinterval = g_strdup (bond_arpinterval_c);
5474 bond_arpiptarget = g_strdup (bond_arpiptarget_c);
5475 bond_lacp_rate = g_strdup (bond_lacp_rate_c);
5477 do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon,
5478 &bond_downdelay, &bond_updelay,
5479 &bond_arpinterval, &bond_arpiptarget,
5482 /* Generate ifname if connection doesn't have one */
5483 ifname = nm_setting_connection_get_interface_name (s_con);
5485 char *bond_ifname = unique_master_iface_ifname (all_connections, "nm-bond");
5487 g_object_set (s_con,
5488 NM_SETTING_CONNECTION_INTERFACE_NAME, bond_ifname,
5490 g_free (bond_ifname);
5493 /* Add 'bond' setting */
5494 s_bond = (NMSettingBond *) nm_setting_bond_new ();
5495 nm_connection_add_setting (connection, NM_SETTING (s_bond));
5497 /* Set bond options */
5501 if (!(bm = nmc_bond_validate_mode (bond_mode, &err))) {
5502 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5503 _("Error: 'mode': %s."), err->message);
5504 g_clear_error (&err);
5507 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, bm);
5510 if (!nm_utils_iface_valid_name (bond_primary)) {
5511 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5512 _("Error: 'primary': '%s' is not a valid interface name."),
5516 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, bond_primary);
5519 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, bond_miimon);
5520 if (bond_downdelay && strcmp (bond_downdelay, "0") != 0)
5521 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, bond_downdelay);
5522 if (bond_updelay && strcmp (bond_updelay, "0") != 0)
5523 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, bond_updelay);
5524 if (bond_arpinterval && strcmp (bond_arpinterval, "0") != 0)
5525 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, bond_arpinterval);
5526 if (bond_arpiptarget)
5527 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, bond_arpiptarget);
5529 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_LACP_RATE, bond_lacp_rate);
5534 g_free (bond_primary);
5535 g_free (bond_miimon);
5536 g_free (bond_downdelay);
5537 g_free (bond_updelay);
5538 g_free (bond_arpinterval);
5539 g_free (bond_arpiptarget);
5540 g_free (bond_lacp_rate);
5544 } else if (!strcmp (con_type, "bond-slave")) {
5545 /* Slave types without any specific settings ('bond-slave') */
5546 const char *master = NULL;
5547 const char *type = NULL;
5548 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
5549 {"type", TRUE, &type, FALSE},
5552 /* Set global variables for use in TAB completion */
5553 nmc_tab_completion.con_type = NM_SETTING_BOND_SETTING_NAME;
5555 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5558 if (!complete_slave (s_con, all_connections, NM_SETTING_BOND_SETTING_NAME, master, type, ask, error))
5561 /* Change properties in 'connection' setting */
5562 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NULL);
5564 /* Add ethernet setting */
5565 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5566 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5568 } else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) {
5569 /* Build up the settings required for 'team' */
5570 gboolean success = FALSE;
5571 const char *ifname = NULL;
5572 const char *config_c = NULL;
5573 char *config = NULL;
5575 nmc_arg_t exp_args[] = { {"config", TRUE, &config_c, FALSE},
5578 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5581 /* Also ask for all optional arguments if '--ask' is specified. */
5582 config = g_strdup (config_c);
5584 do_questionnaire_team (&config);
5586 /* Generate ifname if conneciton doesn't have one */
5587 ifname = nm_setting_connection_get_interface_name (s_con);
5589 char *team_ifname = unique_master_iface_ifname (all_connections, "nm-team");
5591 g_object_set (s_con,
5592 NM_SETTING_CONNECTION_INTERFACE_NAME, team_ifname,
5594 g_free (team_ifname);
5597 /* Add 'team' setting */
5598 s_team = (NMSettingTeam *) nm_setting_team_new ();
5599 nm_connection_add_setting (connection, NM_SETTING (s_team));
5601 if (!nmc_team_check_config (config, &json, error)) {
5602 g_prefix_error (error, _("Error: "));
5606 /* Set team options */
5607 g_object_set (s_team, NM_SETTING_TEAM_CONFIG, json, NULL);
5616 } else if (!strcmp (con_type, "team-slave")) {
5617 /* Build up the settings required for 'team-slave' */
5618 gboolean success = FALSE;
5619 const char *master = NULL;
5620 char *master_ask = NULL;
5621 const char *type = NULL;
5622 const char *config_c = NULL;
5623 char *config = NULL;
5625 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
5626 {"type", TRUE, &type, FALSE},
5627 {"config", TRUE, &config_c, FALSE},
5630 /* Set global variables for use in TAB completion */
5631 nmc_tab_completion.con_type = NM_SETTING_TEAM_SETTING_NAME;
5633 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5636 if (!complete_slave (s_con, all_connections, NM_SETTING_TEAM_SETTING_NAME, master, type, ask, error))
5639 /* Also ask for all optional arguments if '--ask' is specified. */
5640 config = g_strdup (config_c);
5642 do_questionnaire_team_slave (&config);
5644 /* Add 'team-port' setting */
5645 s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new ();
5646 nm_connection_add_setting (connection, NM_SETTING (s_team_port));
5648 if (!nmc_team_check_config (config, &json, error)) {
5649 g_prefix_error (error, _("Error: "));
5650 goto cleanup_team_slave;
5653 /* Set team-port options */
5654 g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, json, NULL);
5658 g_free (master_ask);
5664 /* Change properties in 'connection' setting */
5665 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NULL);
5667 /* Add ethernet setting */
5668 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5669 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5671 } else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
5672 /* Build up the settings required for 'bridge' */
5673 gboolean success = FALSE;
5674 const char *ifname = NULL;
5675 const char *stp_c = NULL;
5677 const char *priority_c = NULL;
5678 char *priority = NULL;
5679 const char *fwd_delay_c = NULL;
5680 char *fwd_delay = NULL;
5681 const char *hello_time_c = NULL;
5682 char *hello_time = NULL;
5683 const char *max_age_c = NULL;
5684 char *max_age = NULL;
5685 const char *ageing_time_c = NULL;
5686 char *ageing_time = NULL;
5687 const char *mcast_snoop_c = NULL;
5688 char *mcast_snoop = NULL;
5689 gboolean stp_bool, mcast_snoop_bool;
5690 unsigned long stp_prio_int, fwd_delay_int, hello_time_int,
5691 max_age_int, ageing_time_int;
5692 const char *mac_c = NULL;
5694 nmc_arg_t exp_args[] = { {"stp", TRUE, &stp_c, FALSE},
5695 {"priority", TRUE, &priority_c, FALSE},
5696 {"forward-delay", TRUE, &fwd_delay_c, FALSE},
5697 {"hello-time", TRUE, &hello_time_c, FALSE},
5698 {"max-age", TRUE, &max_age_c, FALSE},
5699 {"ageing-time", TRUE, &ageing_time_c, FALSE},
5700 {"multicast-snooping", TRUE, &mcast_snoop_c, FALSE},
5701 {"mac", TRUE, &mac_c, FALSE},
5704 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5707 /* Also ask for all optional arguments if '--ask' is specified. */
5708 stp = g_strdup (stp_c);
5709 priority = g_strdup (priority_c);
5710 fwd_delay = g_strdup (fwd_delay_c);
5711 hello_time = g_strdup (hello_time_c);
5712 max_age = g_strdup (max_age_c);
5713 ageing_time = g_strdup (ageing_time_c);
5714 mcast_snoop = g_strdup (mcast_snoop_c);
5715 mac = g_strdup (mac_c);
5717 do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time,
5718 &max_age, &ageing_time, &mcast_snoop, &mac);
5720 /* Generate ifname if conneciton doesn't have one */
5721 ifname = nm_setting_connection_get_interface_name (s_con);
5723 char *bridge_ifname = unique_master_iface_ifname (all_connections, "nm-bridge");
5725 g_object_set (s_con,
5726 NM_SETTING_CONNECTION_INTERFACE_NAME, bridge_ifname,
5728 g_free (bridge_ifname);
5732 GError *tmp_err = NULL;
5733 if (!nmc_string_to_bool (stp, &stp_bool, &tmp_err)) {
5734 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5735 _("Error: 'stp': %s."), tmp_err->message);
5736 g_clear_error (&tmp_err);
5737 goto cleanup_bridge;
5741 GError *tmp_err = NULL;
5742 if (!nmc_string_to_bool (mcast_snoop, &mcast_snoop_bool, &tmp_err)) {
5743 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5744 _("Error: 'multicast-snooping': %s."), tmp_err->message);
5745 g_clear_error (&tmp_err);
5746 goto cleanup_bridge;
5750 /* Add 'bond' setting */
5751 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
5752 s_bridge = (NMSettingBridge *) nm_setting_bridge_new ();
5753 nm_connection_add_setting (connection, NM_SETTING (s_bridge));
5756 if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE,
5757 NM_SETTING_BRIDGE_PRIORITY, &stp_prio_int, error))
5758 goto cleanup_bridge;
5760 if (!bridge_prop_string_to_uint (fwd_delay, "forward-delay", NM_TYPE_SETTING_BRIDGE,
5761 NM_SETTING_BRIDGE_FORWARD_DELAY, &fwd_delay_int, error))
5762 goto cleanup_bridge;
5764 if (!bridge_prop_string_to_uint (hello_time, "hello-time", NM_TYPE_SETTING_BRIDGE,
5765 NM_SETTING_BRIDGE_HELLO_TIME, &hello_time_int, error))
5766 goto cleanup_bridge;
5768 if (!bridge_prop_string_to_uint (max_age, "max-age", NM_TYPE_SETTING_BRIDGE,
5769 NM_SETTING_BRIDGE_MAX_AGE, &max_age_int, error))
5770 goto cleanup_bridge;
5772 if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE,
5773 NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error))
5774 goto cleanup_bridge;
5775 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5776 goto cleanup_bridge;
5778 /* Set bridge options */
5780 g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, stp_bool, NULL);
5782 g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, stp_prio_int, NULL);
5784 g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, fwd_delay_int, NULL);
5786 g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, hello_time_int, NULL);
5788 g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL);
5790 g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL);
5792 g_object_set (s_bridge, NM_SETTING_BRIDGE_MULTICAST_SNOOPING, mcast_snoop_bool, NULL);
5794 g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, NULL);
5801 g_free (hello_time);
5803 g_free (ageing_time);
5804 g_free (mcast_snoop);
5809 } else if (!strcmp (con_type, "bridge-slave")) {
5810 /* Build up the settings required for 'bridge-slave' */
5811 gboolean success = FALSE;
5812 const char *master = NULL;
5813 char *master_ask = NULL;
5814 const char *type = NULL;
5815 const char *priority_c = NULL;
5816 char *priority = NULL;
5817 const char *path_cost_c = NULL;
5818 char *path_cost = NULL;
5819 const char *hairpin_c = NULL;
5820 char *hairpin = NULL;
5821 unsigned long prio_int, path_cost_int;
5822 gboolean hairpin_bool;
5823 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
5824 {"type", TRUE, &type, FALSE},
5825 {"priority", TRUE, &priority_c, FALSE},
5826 {"path-cost", TRUE, &path_cost_c, FALSE},
5827 {"hairpin", TRUE, &hairpin_c, FALSE},
5830 /* Set global variables for use in TAB completion */
5831 nmc_tab_completion.con_type = NM_SETTING_BRIDGE_SETTING_NAME;
5833 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5836 if (!complete_slave (s_con, all_connections, NM_SETTING_BRIDGE_SETTING_NAME, master, type, ask, error))
5839 /* Add 'bridge-port' setting */
5840 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
5841 s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new ();
5842 nm_connection_add_setting (connection, NM_SETTING (s_bridge_port));
5844 /* Also ask for all optional arguments if '--ask' is specified. */
5845 priority = g_strdup (priority_c);
5846 path_cost = g_strdup (path_cost_c);
5847 hairpin = g_strdup (hairpin_c);
5849 do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin);
5852 if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
5853 NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error))
5854 goto cleanup_bridge_slave;
5856 if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
5857 NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error))
5858 goto cleanup_bridge_slave;
5860 GError *tmp_err = NULL;
5861 if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) {
5862 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5863 _("Error: 'hairpin': %s."), tmp_err->message);
5864 g_clear_error (&tmp_err);
5865 goto cleanup_bridge_slave;
5870 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL);
5872 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL);
5874 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL);
5877 cleanup_bridge_slave:
5878 g_free (master_ask);
5885 /* Change properties in 'connection' setting */
5886 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NULL);
5888 /* Add ethernet setting */
5889 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5890 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5892 } else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) {
5893 /* Build up the settings required for 'vpn' */
5894 gboolean success = FALSE;
5895 const char *vpn_type = NULL;
5896 char *vpn_type_ask = NULL;
5897 const char *user_c = NULL;
5900 char *service_type = NULL;
5901 nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask},
5902 {"user", TRUE, &user_c, FALSE},
5905 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5908 if (!vpn_type && ask)
5909 vpn_type = vpn_type_ask = nmc_readline (PROMPT_VPN_TYPE);
5911 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5912 _("Error: 'vpn-type' is required."));
5916 vpn_type = g_strstrip (vpn_type_ask);
5918 if (!(st = nmc_string_is_valid (vpn_type, nmc_known_vpns, NULL))) {
5919 g_print (_("Warning: 'vpn-type': %s not known.\n"), vpn_type);
5922 service_type = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, st);
5924 /* Also ask for all optional arguments if '--ask' is specified. */
5925 user = g_strdup (user_c);
5927 do_questionnaire_vpn (&user);
5929 /* Add 'vpn' setting */
5930 s_vpn = (NMSettingVpn *) nm_setting_vpn_new ();
5931 nm_connection_add_setting (connection, NM_SETTING (s_vpn));
5933 g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL);
5934 g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL);
5938 g_free (vpn_type_ask);
5939 g_free (service_type);
5944 } else if (!strcmp (con_type, NM_SETTING_OLPC_MESH_SETTING_NAME)) {
5945 /* Build up the settings required for 'olpc' */
5946 gboolean success = FALSE;
5947 char *ssid_ask = NULL;
5948 const char *ssid = NULL;
5950 const char *channel_c = NULL;
5951 char *channel = NULL;
5953 const char *dhcp_anycast_c = NULL;
5954 char *dhcp_anycast = NULL;
5955 nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask},
5956 {"channel", TRUE, &channel_c, FALSE},
5957 {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE},
5960 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5964 ssid = ssid_ask = nmc_readline (_("SSID: "));
5966 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5967 _("Error: 'ssid' is required."));
5971 /* Also ask for all optional arguments if '--ask' is specified. */
5972 channel = g_strdup (channel_c);
5973 dhcp_anycast = g_strdup (dhcp_anycast_c);
5975 do_questionnaire_olpc (&channel, &dhcp_anycast);
5978 if (!nmc_string_to_uint (channel, TRUE, 1, 13, &chan)) {
5979 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5980 _("Error: 'channel': '%s' is not valid; use <1-13>."),
5985 if (!check_mac (dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", error))
5988 /* Add OLPC mesh setting */
5989 s_olpc_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new ();
5990 nm_connection_add_setting (connection, NM_SETTING (s_olpc_mesh));
5992 ssid_bytes = g_bytes_new (ssid, strlen (ssid));
5993 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_SSID, ssid_bytes, NULL);
5995 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, chan, NULL);
5997 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, 1, NULL);
5999 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, dhcp_anycast, NULL);
6000 g_bytes_unref (ssid_bytes);
6006 g_free (dhcp_anycast);
6010 } else if (!strcmp (con_type, NM_SETTING_ADSL_SETTING_NAME)) {
6011 /* Build up the settings required for 'adsl' */
6012 gboolean success = FALSE;
6013 char *username_ask = NULL;
6014 const char *username = NULL;
6015 char *protocol_ask = NULL, *protocol = NULL;
6016 const char *protocol_c = NULL;
6017 const char *password_c = NULL;
6018 char *password = NULL;
6019 const char *encapsulation_c = NULL;
6020 char *encapsulation = NULL;
6021 nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask},
6022 {"protocol", TRUE, &protocol_c, !ask},
6023 {"password", TRUE, &password_c, FALSE},
6024 {"encapsulation", TRUE, &encapsulation_c, FALSE},
6027 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6030 if (!username && ask)
6031 username = username_ask = nmc_readline (_("Username: "));
6033 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6034 _("Error: 'username' is required."));
6038 #define PROMPT_ADSL_PROTO "(" NM_SETTING_ADSL_PROTOCOL_PPPOA "/" NM_SETTING_ADSL_PROTOCOL_PPPOE "/" NM_SETTING_ADSL_PROTOCOL_IPOATM "): "
6039 if (!protocol_c && ask)
6040 protocol_c = protocol_ask = nmc_readline (_("Protocol %s"), PROMPT_ADSL_PROTO);
6042 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6043 _("Error: 'protocol' is required."));
6046 protocol = g_strdup (protocol_c);
6047 if (!check_adsl_protocol (&protocol, error))
6050 /* Also ask for all optional arguments if '--ask' is specified. */
6051 password = g_strdup (password_c);
6052 encapsulation = g_strdup (encapsulation_c);
6054 do_questionnaire_adsl (show_secrets, &password, &encapsulation);
6056 if (!check_adsl_encapsulation (&encapsulation, error))
6059 /* Add ADSL setting */
6060 s_adsl = (NMSettingAdsl *) nm_setting_adsl_new ();
6061 nm_connection_add_setting (connection, NM_SETTING (s_adsl));
6063 g_object_set (s_adsl,
6064 NM_SETTING_ADSL_USERNAME, username,
6065 NM_SETTING_ADSL_PROTOCOL, protocol,
6066 NM_SETTING_ADSL_PASSWORD, password,
6067 NM_SETTING_ADSL_ENCAPSULATION, encapsulation,
6072 g_free (username_ask);
6075 g_free (protocol_ask);
6076 g_free (encapsulation);
6081 } else if (!strcmp (con_type, NM_SETTING_MACVLAN_SETTING_NAME)) {
6082 /* Build up the settings required for 'macvlan' */
6083 gboolean success = FALSE;
6084 const char *parent = NULL;
6085 char *parent_ask = NULL;
6086 const char *mode = NULL;
6087 char *mode_ask = NULL;
6088 const char *tap_c = NULL;
6090 NMSettingMacvlanMode mode_enum;
6091 gboolean valid_mac = FALSE;
6092 gboolean tap_bool = FALSE;
6093 nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask},
6094 {"mode", TRUE, &mode, !ask},
6095 {"tap", TRUE, &tap_c, FALSE},
6098 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6102 parent = parent_ask = nmc_readline (_("MACVLAN parent device or connection UUID: "));
6104 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6105 _("Error: 'dev' is required."));
6109 if ( !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN))
6110 && !nm_utils_is_uuid (parent)
6111 && !nm_utils_iface_valid_name (parent)) {
6112 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6113 _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
6115 goto cleanup_macvlan;
6119 mode = mode_ask = nmc_readline (PROMPT_MACVLAN_MODE);
6121 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6122 _("Error: 'mode' is required."));
6126 if (!nm_utils_enum_from_str (nm_setting_macvlan_mode_get_type(), mode, (int *) &mode_enum, NULL)) {
6127 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6128 _("Error: 'mode' is not valid."));
6132 /* Also ask for all optional arguments if '--ask' is specified. */
6133 tap = g_strdup (tap_c);
6135 do_questionnaire_macvlan (&tap);
6138 GError *tmp_err = NULL;
6139 if (!nmc_string_to_bool (tap, &tap_bool, &tmp_err)) {
6140 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6141 _("Error: 'tap': %s."), tmp_err->message);
6142 g_clear_error (&tmp_err);
6143 goto cleanup_macvlan;
6147 /* Add 'macvlan' setting */
6148 s_macvlan = (NMSettingMacvlan *) nm_setting_macvlan_new ();
6149 nm_connection_add_setting (connection, NM_SETTING (s_macvlan));
6151 /* Add 'wired' setting if necessary */
6153 s_wired = (NMSettingWired *) nm_setting_wired_new ();
6154 nm_connection_add_setting (connection, NM_SETTING (s_wired));
6155 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL);
6158 /* Set 'macvlan' properties */
6160 g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, parent, NULL);
6161 g_object_set (s_macvlan, NM_SETTING_MACVLAN_MODE, mode_enum, NULL);
6162 g_object_set (s_macvlan, NM_SETTING_MACVLAN_TAP, tap_bool, NULL);
6166 g_free (parent_ask);
6173 } else if (!strcmp (con_type, NM_SETTING_TUN_SETTING_NAME)) {
6174 /* Build up the settings required for 'tun' */
6175 gboolean success = FALSE;
6176 const char *mode_c = NULL;
6177 char *mode_ask = NULL, *mode = NULL;
6178 NMSettingTunMode mode_enum;
6179 const char *owner_c = NULL, *group_c = NULL;
6180 char *owner = NULL, *group = NULL;
6181 const char *pi_c = NULL, *vnet_hdr_c = NULL, *multi_queue_c = NULL;
6182 char *pi = NULL, *vnet_hdr = NULL, *multi_queue = NULL;
6183 gboolean pi_bool, vnet_hdr_bool, multi_queue_bool;
6184 nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !ask},
6185 {"owner", TRUE, &owner_c, FALSE},
6186 {"group", TRUE, &group_c, FALSE},
6187 {"pi", TRUE, &pi_c, FALSE},
6188 {"vnet-hdr", TRUE, &vnet_hdr_c, FALSE},
6189 {"multi-queue", TRUE, &multi_queue_c, FALSE},
6192 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6195 if (!mode_c && ask) {
6196 mode_ask = nmc_readline (_("Mode %s"), PROMPT_TUN_MODE);
6197 mode_ask = mode_ask ? mode_ask : g_strdup ("tun");
6201 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6202 _("Error: 'mode' is required."));
6205 mode = g_strdup (mode_c);
6206 if (!check_tun_mode (&mode, error))
6209 if (owner && !check_user_group_id (owner, error))
6211 if (group && !check_user_group_id (group, error))
6214 owner = g_strdup (owner_c);
6215 group = g_strdup (group_c);
6216 pi = g_strdup (pi_c);
6217 vnet_hdr = g_strdup (vnet_hdr_c);
6218 multi_queue = g_strdup (multi_queue_c);
6220 do_questionnaire_tun (&owner, &group, &pi, &vnet_hdr, &multi_queue);
6223 GError *tmp_err = NULL;
6225 if (!nmc_string_to_bool (pi, &pi_bool, &tmp_err)) {
6226 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6227 _("Error: 'pi': %s."), tmp_err->message);
6228 g_clear_error (&tmp_err);
6234 GError *tmp_err = NULL;
6236 if (!nmc_string_to_bool (vnet_hdr, &vnet_hdr_bool, &tmp_err)) {
6237 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6238 _("Error: 'vnet-hdr': %s."), tmp_err->message);
6239 g_clear_error (&tmp_err);
6245 GError *tmp_err = NULL;
6247 if (!nmc_string_to_bool (multi_queue, &multi_queue_bool, &tmp_err)) {
6248 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6249 _("Error: 'multi-queue': %s."), tmp_err->message);
6250 g_clear_error (&tmp_err);
6254 /* Add 'tun' setting */
6255 s_tun = (NMSettingTun *) nm_setting_tun_new ();
6256 nm_connection_add_setting (connection, NM_SETTING (s_tun));
6257 mode_enum = !strcmp (mode, "tun") ? NM_SETTING_TUN_MODE_TUN : NM_SETTING_TUN_MODE_TAP;
6259 g_object_set (s_tun,
6260 NM_SETTING_TUN_MODE, mode_enum,
6261 NM_SETTING_TUN_OWNER, owner,
6262 NM_SETTING_TUN_GROUP, group,
6265 g_object_set (s_tun, NM_SETTING_TUN_PI, pi_bool, NULL);
6267 g_object_set (s_tun, NM_SETTING_TUN_VNET_HDR, vnet_hdr_bool, NULL);
6269 g_object_set (s_tun, NM_SETTING_TUN_MULTI_QUEUE, multi_queue_bool, NULL);
6279 g_free (multi_queue);
6283 } else if (!strcmp (con_type, NM_SETTING_IP_TUNNEL_SETTING_NAME)) {
6284 /* Build up the settings required for 'ip-tunnel' */
6285 const char *mode_c = NULL, *local_c = NULL, *remote_c = NULL;
6286 char *mode_ask = NULL, *remote_ask = NULL, *local = NULL;
6287 const char *parent_c = NULL;
6288 char *parent = NULL;
6289 gboolean success = FALSE;
6290 NMIPTunnelMode mode_enum;
6291 nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !ask},
6292 {"local", TRUE, &local_c, FALSE},
6293 {"remote", TRUE, &remote_c, !ask},
6294 {"dev", TRUE, &parent_c, FALSE},
6297 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6301 mode_c = mode_ask = nmc_readline (PROMPT_IP_TUNNEL_MODE);
6303 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6304 _("Error: 'mode' is required."));
6305 goto cleanup_tunnel;
6308 if (!nm_utils_enum_from_str (nm_ip_tunnel_mode_get_type (),
6309 mode_c, (int *) &mode_enum, NULL)) {
6310 gs_free const char **values = NULL;
6311 gs_free char *values_str = NULL;
6313 values = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
6314 NM_IP_TUNNEL_MODE_UNKNOWN + 1,
6316 values_str = g_strjoinv (",", (char **) values);
6317 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6318 _("Error: 'mode': '%s' is not valid, use one of %s"),
6319 mode_c, values_str);
6320 goto cleanup_tunnel;
6323 if (!remote_c && ask)
6324 remote_c = remote_ask = nmc_readline (_("Remote endpoint: "));
6326 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6327 _("Error: 'remote' is required."));
6328 goto cleanup_tunnel;
6331 if ( !nm_utils_ipaddr_valid (AF_INET, remote_c)
6332 && !nm_utils_ipaddr_valid (AF_INET6, remote_c)) {
6333 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6334 _("Error: 'remote': '%s' is not valid; must be an IP address"),
6336 goto cleanup_tunnel;
6339 local = g_strdup (local_c);
6340 parent = g_strdup (parent_c);
6342 do_questionnaire_ip_tunnel (&local, &parent);
6345 && !nm_utils_ipaddr_valid (AF_INET, local)
6346 && !nm_utils_ipaddr_valid (AF_INET6, local)) {
6347 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6348 _("Error: 'local': '%s' is not valid; must be an IP address"),
6350 goto cleanup_tunnel;
6354 if ( !nm_utils_is_uuid (parent)
6355 && !nm_utils_iface_valid_name (parent)) {
6356 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6357 _("Error: 'dev': '%s' is neither UUID nor interface name."),
6359 goto cleanup_tunnel;
6363 /* Add 'tunnel' setting */
6364 s_ip_tunnel = (NMSettingIPTunnel *) nm_setting_ip_tunnel_new ();
6365 nm_connection_add_setting (connection, NM_SETTING (s_ip_tunnel));
6367 /* Set 'tunnel' properties */
6368 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_MODE, mode_enum, NULL);
6369 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_REMOTE, remote_c, NULL);
6371 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_LOCAL, local, NULL);
6373 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_PARENT, parent, NULL);
6375 /* Set default values for IPv6 tunnels */
6376 if (nm_utils_ipaddr_valid (AF_INET6, remote_c)) {
6377 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_TOS, 64, NULL);
6378 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT, 4, NULL);
6383 g_free (remote_ask);
6390 } else if (!strcmp (con_type, NM_SETTING_VXLAN_SETTING_NAME)) {
6391 /* Build up the settings required for 'vxlan' */
6392 gboolean success = FALSE;
6393 char *id_ask = NULL;
6394 const char *id = NULL;
6395 char *remote_ask = NULL;
6396 const char *remote = NULL;
6397 const char *parent_c = NULL, *local_c = NULL;
6398 const char *src_port_min_c = NULL, *src_port_max_c = NULL;
6399 const char *dst_port_c = NULL;
6400 char *parent = NULL, *local = NULL;
6401 char *src_port_min = NULL, *src_port_max = NULL, *dst_port = NULL;
6402 unsigned long int vni;
6403 unsigned long sport_min = G_MAXULONG, sport_max = G_MAXULONG;
6404 unsigned long dport = G_MAXULONG;
6405 nmc_arg_t exp_args[] = { {"id", TRUE, &id, !ask},
6406 {"remote", TRUE, &remote, !ask},
6407 {"dev", TRUE, &parent_c, FALSE},
6408 {"local", TRUE, &local_c, FALSE},
6409 {"source-port-min", TRUE, &src_port_min_c, FALSE},
6410 {"source-port-max", TRUE, &src_port_max_c, FALSE},
6411 {"destination-port", TRUE, &dst_port_c, FALSE},
6414 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6418 id = id_ask = nmc_readline (_("VXLAN ID: "));
6420 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6421 _("Error: 'id' is required."));
6426 remote = remote_ask = nmc_readline (_("Remote: "));
6428 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6429 _("Error: 'remote' is required."));
6433 if (!nmc_string_to_uint (id, TRUE, 0, (1UL << 24) - 1, &vni)) {
6434 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6435 _("Error: 'id': '%s' is not valid; use <0-16777215>."), id);
6439 parent = g_strdup (parent_c);
6440 local = g_strdup (local_c);
6441 src_port_min = g_strdup (src_port_min_c);
6442 src_port_max = g_strdup (src_port_max_c);
6443 dst_port = g_strdup (dst_port_c);
6446 do_questionnaire_vxlan (&parent, &local, &src_port_min, &src_port_max, &dst_port);
6449 if ( !nm_utils_is_uuid (parent)
6450 && !nm_utils_iface_valid_name (parent)) {
6451 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6452 _("Error: 'dev': '%s' is neither UUID nor interface name."),
6458 if ( !nm_utils_ipaddr_valid (AF_INET, remote)
6459 && !nm_utils_ipaddr_valid (AF_INET6, remote)) {
6460 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6461 _("Error: 'remote': '%s' is not a valid IP address"),
6467 if ( !nm_utils_ipaddr_valid (AF_INET, local)
6468 && !nm_utils_ipaddr_valid (AF_INET6, local)) {
6469 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6470 _("Error: 'local': '%s' is not a valid IP address"),
6477 if (!nmc_string_to_uint (src_port_min, TRUE, 0, 65535, &sport_min)) {
6478 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6479 _("Error: 'source-port-min': %s is not valid; use <0-65535>."),
6486 if (!nmc_string_to_uint (src_port_max, TRUE, 0, 65535, &sport_max)) {
6487 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6488 _("Error: 'source-port-max': %s is not valid; use <0-65535>."),
6495 if (!nmc_string_to_uint (dst_port, TRUE, 0, 65535, &dport)) {
6496 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6497 _("Error: 'destination-port': %s is not valid; use <0-65535>."),
6503 /* Add 'vxlan' setting */
6504 s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new ();
6505 nm_connection_add_setting (connection, NM_SETTING (s_vxlan));
6507 g_object_set (s_vxlan, NM_SETTING_VXLAN_ID, (guint) vni, NULL);
6508 g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE, remote, NULL);
6509 g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL, local, NULL);
6510 g_object_set (s_vxlan, NM_SETTING_VXLAN_PARENT, parent, NULL);
6512 if (sport_min != G_MAXULONG)
6513 g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MIN, sport_min, NULL);
6514 if (sport_max != G_MAXULONG)
6515 g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MAX, sport_max, NULL);
6516 if (dport != G_MAXULONG)
6517 g_object_set (s_vxlan, NM_SETTING_VXLAN_DESTINATION_PORT, dport, NULL);
6523 g_free (remote_ask);
6526 g_free (src_port_min);
6527 g_free (src_port_max);
6531 } else if (!strcmp (con_type, NM_SETTING_GENERIC_SETTING_NAME)) {
6532 /* Add 'generic' setting */
6533 s_generic = (NMSettingGeneric *) nm_setting_generic_new ();
6534 nm_connection_add_setting (connection, NM_SETTING (s_generic));
6536 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6537 _("Error: '%s' is not a valid connection type."),
6542 if (!nm_setting_connection_get_slave_type (s_con)) {
6543 /* Read and add IP configuration */
6544 NMIPAddress *ip4addr = NULL, *ip6addr = NULL;
6545 const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL;
6546 nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE},
6547 {"ip6", TRUE, &ip6, FALSE}, {"gw6", TRUE, &gw6, FALSE},
6553 /* reset 'found' flag */
6554 for (p = exp_args; p->name; p++)
6557 ip4 = gw4 = ip6 = gw6 = NULL;
6559 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, NULL))
6562 /* coverity[dead_error_begin] */
6564 ip4addr = nmc_parse_and_build_address (AF_INET, ip4, error);
6566 g_prefix_error (error, _("Error: "));
6569 add_ip4_address_to_connection (ip4addr, connection);
6572 /* coverity[dead_error_begin] */
6574 NMSettingIPConfig *s_ip = nm_connection_get_setting_ip4_config (connection);
6577 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6578 _("Error: IPv4 gateway specified without IPv4 addresses"));
6580 } else if (nm_setting_ip_config_get_gateway (s_ip)) {
6581 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6582 _("Error: multiple IPv4 gateways specified"));
6584 } else if (!nm_utils_ipaddr_valid (AF_INET, gw4)) {
6585 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6586 _("Error: Invalid IPv4 gateway '%s'"),
6591 NM_SETTING_IP_CONFIG_GATEWAY, gw4,
6595 /* coverity[dead_error_begin] */
6597 ip6addr = nmc_parse_and_build_address (AF_INET6, ip6, error);
6599 g_prefix_error (error, _("Error: "));
6602 add_ip6_address_to_connection (ip6addr, connection);
6605 /* coverity[dead_error_begin] */
6607 NMSettingIPConfig *s_ip = nm_connection_get_setting_ip6_config (connection);
6610 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6611 _("Error: IPv6 gateway specified without IPv6 addresses"));
6613 } else if (nm_setting_ip_config_get_gateway (s_ip)) {
6614 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6615 _("Error: multiple IPv6 gateways specified"));
6617 } else if (!nm_utils_ipaddr_valid (AF_INET, gw6)) {
6618 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6619 _("Error: Invalid IPv6 gateway '%s'"),
6624 NM_SETTING_IP_CONFIG_GATEWAY, gw6,
6629 /* Ask for addresses if '--ask' is specified. */
6631 do_questionnaire_ip (connection);
6635 /* Set extra connection properties. */
6636 nmc_arg_t exp_args[] = { {"--", FALSE, NULL, TRUE},
6639 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6642 if (!read_connection_properties (connection, argc, argv, error))
6652 } AddConnectionInfo;
6655 add_connection_cb (GObject *client,
6656 GAsyncResult *result,
6659 AddConnectionInfo *info = (AddConnectionInfo *) user_data;
6660 NmCli *nmc = info->nmc;
6661 NMRemoteConnection *connection;
6662 GError *error = NULL;
6664 connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
6666 g_string_printf (nmc->return_text,
6667 _("Error: Failed to add '%s' connection: %s"),
6668 info->con_name, error->message);
6669 g_error_free (error);
6670 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
6672 g_print (_("Connection '%s' (%s) successfully added.\n"),
6673 nm_connection_get_id (NM_CONNECTION (connection)),
6674 nm_connection_get_uuid (NM_CONNECTION (connection)));
6675 g_object_unref (connection);
6678 g_free (info->con_name);
6684 add_new_connection (gboolean persistent,
6686 NMConnection *connection,
6687 GAsyncReadyCallback callback,
6690 nm_client_add_connection_async (client, connection, persistent,
6691 NULL, callback, user_data);
6695 update_connection (gboolean persistent,
6696 NMRemoteConnection *connection,
6697 GAsyncReadyCallback callback,
6700 nm_remote_connection_commit_changes_async (connection, persistent,
6701 NULL, callback, user_data);
6705 gen_func_vpn_types (const char *text, int state)
6707 return nmc_rl_gen_func_basic (text, state, nmc_known_vpns);
6711 gen_func_bool_values_l10n (const char *text, int state)
6713 const char *words[] = { WORD_LOC_YES, WORD_LOC_NO, NULL };
6714 return nmc_rl_gen_func_basic (text, state, words);
6718 gen_func_wifi_mode (const char *text, int state)
6720 const char *words[] = { "infrastructure", "ap", "adhoc", NULL };
6721 return nmc_rl_gen_func_basic (text, state, words);
6725 gen_func_ib_type (const char *text, int state)
6727 const char *words[] = { "datagram", "connected", NULL };
6728 return nmc_rl_gen_func_basic (text, state, words);
6732 gen_func_bt_type (const char *text, int state)
6734 const char *words[] = { "panu", "dun-gsm", "dun-cdma", NULL };
6735 return nmc_rl_gen_func_basic (text, state, words);
6739 gen_func_bond_mode (const char *text, int state)
6741 const char *words[] = { "balance-rr", "active-backup", "balance-xor", "broadcast",
6742 "802.3ad", "balance-tlb", "balance-alb", NULL };
6743 return nmc_rl_gen_func_basic (text, state, words);
6746 gen_func_bond_mon_mode (const char *text, int state)
6748 const char *words[] = { "miimon", "arp", NULL };
6749 return nmc_rl_gen_func_basic (text, state, words);
6753 gen_func_adsl_proto (const char *text, int state)
6755 const char *words[] = { "pppoe", "pppoa", "ipoatm", NULL };
6756 return nmc_rl_gen_func_basic (text, state, words);
6760 gen_func_adsl_encap (const char *text, int state)
6762 const char *words[] = { "vcmux", "llc", NULL };
6763 return nmc_rl_gen_func_basic (text, state, words);
6767 gen_func_tun_mode (const char *text, int state)
6769 const char *words[] = { "tun", "tap", NULL };
6770 return nmc_rl_gen_func_basic (text, state, words);
6774 gen_func_ip_tunnel_mode (const char *text, int state)
6776 gs_free const char **words = NULL;
6778 words = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
6779 NM_IP_TUNNEL_MODE_UNKNOWN + 1,
6781 return nmc_rl_gen_func_basic (text, state, words);
6785 gen_func_macvlan_mode (const char *text, int state)
6787 gs_free const char **words = NULL;
6789 words = nm_utils_enum_get_values (nm_setting_macvlan_mode_get_type(),
6790 NM_SETTING_MACVLAN_MODE_UNKNOWN + 1,
6792 return nmc_rl_gen_func_basic (text, state, words);
6796 gen_func_master_ifnames (const char *text, int state)
6802 NMSettingConnection *s_con;
6803 const char *con_type, *ifname;
6805 if (!nm_cli.connections)
6808 /* Disable appending space after completion */
6809 rl_completion_append_character = '\0';
6811 ifnames = g_ptr_array_sized_new (20);
6812 for (i = 0; i < nm_cli.connections->len; i++) {
6813 con = NM_CONNECTION (nm_cli.connections->pdata[i]);
6814 s_con = nm_connection_get_setting_connection (con);
6816 con_type = nm_setting_connection_get_connection_type (s_con);
6817 if (g_strcmp0 (con_type, nmc_tab_completion.con_type) != 0)
6819 ifname = nm_connection_get_interface_name (con);
6820 g_ptr_array_add (ifnames, (gpointer) ifname);
6822 g_ptr_array_add (ifnames, (gpointer) NULL);
6824 ret = nmc_rl_gen_func_basic (text, state, (const char **) ifnames->pdata);
6826 g_ptr_array_free (ifnames, TRUE);
6831 is_single_word (const char* line)
6835 n1 = strspn (line, " \t");
6836 n2 = strcspn (line+n1, " \t\0") + n1;
6837 n3 = strspn (line+n2, " \t");
6846 nmcli_con_add_tab_completion (const char *text, int start, int end)
6848 char **match_array = NULL;
6849 rl_compentry_func_t *generator_func = NULL;
6851 /* Disable readline's default filename completion */
6852 rl_attempted_completion_over = 1;
6854 /* Restore standard append character to space */
6855 rl_completion_append_character = ' ';
6857 if (!is_single_word (rl_line_buffer))
6860 if (g_strcmp0 (rl_prompt, PROMPT_CON_TYPE) == 0)
6861 generator_func = gen_connection_types;
6862 else if (g_strcmp0 (rl_prompt, PROMPT_VPN_TYPE) == 0)
6863 generator_func = gen_func_vpn_types;
6864 else if (g_strcmp0 (rl_prompt, PROMPT_MASTER) == 0)
6865 generator_func = gen_func_master_ifnames;
6866 else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL))
6867 || g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, ":"))
6868 || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL))
6869 || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, ":")))
6870 generator_func = gen_func_bool_values_l10n;
6871 else if (g_str_has_suffix (rl_prompt, PROMPT_WIFI_MODE))
6872 generator_func = gen_func_wifi_mode;
6873 else if (g_str_has_suffix (rl_prompt, PROMPT_IB_MODE))
6874 generator_func = gen_func_ib_type;
6875 else if (g_str_has_suffix (rl_prompt, PROMPT_BT_TYPE))
6876 generator_func = gen_func_bt_type;
6877 else if (g_str_has_prefix (rl_prompt, PROMPT_BOND_MODE))
6878 generator_func = gen_func_bond_mode;
6879 else if (g_str_has_suffix (rl_prompt, PROMPT_BOND_MON_MODE))
6880 generator_func = gen_func_bond_mon_mode;
6881 else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_PROTO))
6882 generator_func = gen_func_adsl_proto;
6883 else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_ENCAP))
6884 generator_func = gen_func_adsl_encap;
6885 else if (g_str_has_suffix (rl_prompt, PROMPT_TUN_MODE))
6886 generator_func = gen_func_tun_mode;
6887 else if (g_str_has_suffix (rl_prompt, PROMPT_IP_TUNNEL_MODE))
6888 generator_func = gen_func_ip_tunnel_mode;
6889 else if (g_str_has_suffix (rl_prompt, PROMPT_MACVLAN_MODE))
6890 generator_func = gen_func_macvlan_mode;
6893 match_array = rl_completion_matches (text, generator_func);
6898 static NMCResultCode
6899 do_connection_add (NmCli *nmc, int argc, char **argv)
6901 NMConnection *connection = NULL;
6902 NMSettingConnection *s_con;
6904 char *default_name = NULL;
6905 const char *type = NULL;
6906 char *type_ask = NULL;
6907 const char *con_name = NULL;
6908 const char *autoconnect = NULL;
6909 gboolean auto_bool = TRUE;
6910 const char *ifname = NULL;
6911 char *ifname_ask = NULL;
6912 gboolean ifname_mandatory = TRUE;
6913 const char *save = NULL;
6914 gboolean save_bool = TRUE;
6915 const char *master = NULL;
6916 const char *checked_master = NULL;
6917 const char *slave_type = NULL;
6918 AddConnectionInfo *info = NULL;
6919 const char *setting_name;
6920 GError *error = NULL;
6921 nmc_arg_t exp_args[] = { {"type", TRUE, &type, !nmc->ask},
6922 {"con-name", TRUE, &con_name, FALSE},
6923 {"autoconnect", TRUE, &autoconnect, FALSE},
6924 {"ifname", TRUE, &ifname, FALSE},
6925 {"save", TRUE, &save, FALSE},
6926 {"master", TRUE, &master, FALSE},
6927 {"slave-type", TRUE, &slave_type, FALSE},
6930 rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion;
6932 nmc->return_value = NMC_RESULT_SUCCESS;
6934 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) {
6935 g_string_assign (nmc->return_text, error->message);
6936 nmc->return_value = error->code;
6937 g_clear_error (&error);
6941 if (!type && nmc->ask) {
6942 char *types_tmp = get_valid_options_string (nmc_valid_connection_types, NULL);
6943 g_print ("Valid types: [%s]\n", types_tmp);
6944 type = type_ask = nmc_readline (PROMPT_CON_TYPE);
6948 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
6949 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6953 type = g_strstrip (type_ask);
6955 if (!(setting_name = check_valid_name (type, nmc_valid_connection_types, NULL, &error))) {
6956 g_string_printf (nmc->return_text, _("Error: invalid connection type; %s."),
6958 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6959 g_clear_error (&error);
6963 GError *tmp_err = NULL;
6964 if (!nmc_string_to_bool (autoconnect, &auto_bool, &tmp_err)) {
6965 g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."),
6967 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6968 g_clear_error (&tmp_err);
6973 GError *tmp_err = NULL;
6974 if (!nmc_string_to_bool (save, &save_bool, &tmp_err)) {
6975 g_string_printf (nmc->return_text, _("Error: 'save': %s."),
6977 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6978 g_clear_error (&tmp_err);
6983 /* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */
6984 if ( strcmp (type, NM_SETTING_BOND_SETTING_NAME) == 0
6985 || strcmp (type, NM_SETTING_TEAM_SETTING_NAME) == 0
6986 || strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME) == 0
6987 || strcmp (type, NM_SETTING_VLAN_SETTING_NAME) == 0)
6988 ifname_mandatory = FALSE;
6990 if (!ifname && ifname_mandatory) {
6992 ifname = ifname_ask = nmc_readline (_("Interface name [*]: "));
6994 ifname = ifname_ask = g_strdup ("*");
6997 g_string_printf (nmc->return_text, _("Error: 'ifname' argument is required."));
6999 g_string_printf (nmc->return_text, _("Error: mandatory 'ifname' not seen before '%s'."),
7001 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7006 if (!nm_utils_iface_valid_name (ifname) && strcmp (ifname, "*") != 0) {
7007 g_string_printf (nmc->return_text,
7008 _("Error: 'ifname': '%s' is not a valid interface nor '*'."),
7010 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7013 /* Special value of '*' means no specific interface name */
7014 if (strcmp (ifname, "*") == 0)
7018 /* Create a new connection object */
7019 connection = nm_simple_connection_new ();
7021 /* Build up the 'connection' setting */
7022 s_con = (NMSettingConnection *) nm_setting_connection_new ();
7023 uuid = nm_utils_uuid_generate ();
7025 default_name = g_strdup (con_name);
7027 char *try_name = ifname ?
7028 g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname)
7029 : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types));
7030 default_name = nmc_unique_connection_name (nmc->connections, try_name);
7035 /* Verify master argument */
7036 checked_master = normalized_master_for_slave (nmc->connections, master, slave_type, &slave_type);
7038 g_object_set (s_con,
7039 NM_SETTING_CONNECTION_ID, default_name,
7040 NM_SETTING_CONNECTION_UUID, uuid,
7041 NM_SETTING_CONNECTION_TYPE, setting_name,
7042 NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool,
7043 NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
7044 NM_SETTING_CONNECTION_MASTER, checked_master,
7045 NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
7048 g_free (default_name);
7049 nm_connection_add_setting (connection, NM_SETTING (s_con));
7051 if (!complete_connection_by_type (connection,
7059 g_string_assign (nmc->return_text, error->message);
7060 nmc->return_value = error->code;
7061 g_clear_error (&error);
7067 info = g_malloc0 (sizeof (AddConnectionInfo));
7069 info->con_name = g_strdup (nm_connection_get_id (connection));
7071 /* Tell the settings service to add the new connection */
7072 add_new_connection (save_bool,
7079 g_object_unref (connection);
7081 return nmc->return_value;
7085 g_object_unref (connection);
7087 g_free (ifname_ask);
7089 return nmc->return_value;
7093 /*----------------------------------------------------------------------------*/
7094 /* Functions for readline TAB completion in editor */
7097 uuid_display_hook (char **array, int len, int max_len)
7103 for (i = 1; i <= len; i++) {
7104 con = nmc_find_connection (nmc_tab_completion.nmc->connections, "uuid", array[i], NULL);
7105 id = con ? nm_connection_get_id (con) : NULL;
7107 tmp = g_strdup_printf ("%s (%s)", array[i], id);
7110 if (max < strlen (id))
7114 rl_display_match_list (array, len, max_len + max + 3);
7115 rl_forced_update_display ();
7119 gen_nmcli_cmds_menu (const char *text, int state)
7121 const char *words[] = { "goto", "set", "remove", "describe", "print", "verify",
7122 "save", "activate", "back", "help", "quit", "nmcli",
7124 return nmc_rl_gen_func_basic (text, state, words);
7128 gen_nmcli_cmds_submenu (const char *text, int state)
7130 const char *words[] = { "set", "add", "change", "remove", "describe",
7131 "print", "back", "help", "quit",
7133 return nmc_rl_gen_func_basic (text, state, words);
7137 gen_cmd_nmcli (const char *text, int state)
7139 const char *words[] = { "status-line", "save-confirmation", "show-secrets", "prompt-color", NULL };
7140 return nmc_rl_gen_func_basic (text, state, words);
7144 gen_cmd_nmcli_prompt_color (const char *text, int state)
7146 const char *words[] = { "normal", "black", "red", "green", "yellow",
7147 "blue", "magenta", "cyan", "white", NULL };
7148 return nmc_rl_gen_func_basic (text, state, words);
7152 gen_func_bool_values (const char *text, int state)
7154 const char *words[] = { "yes", "no", NULL };
7155 return nmc_rl_gen_func_basic (text, state, words);
7159 gen_cmd_verify0 (const char *text, int state)
7161 const char *words[] = { "all", "fix", NULL };
7162 return nmc_rl_gen_func_basic (text, state, words);
7166 gen_cmd_print0 (const char *text, int state)
7168 static char **words = NULL;
7174 const char *setting_name;
7177 settings = nm_connection_to_dbus (nmc_tab_completion.connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
7178 words = g_new (char *, g_variant_n_children (settings) + 2);
7179 g_variant_iter_init (&iter, settings);
7180 while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, NULL))
7181 words [i++] = g_strdup (setting_name);
7182 words[i++] = g_strdup ("all");
7184 g_variant_unref (settings);
7188 ret = nmc_rl_gen_func_basic (text, state, (const char **) words);
7198 gen_cmd_print2 (const char *text, int state)
7200 const char *words[] = { "setting", "connection", "all", NULL };
7201 return nmc_rl_gen_func_basic (text, state, words);
7205 gen_cmd_save (const char *text, int state)
7207 const char *words[] = { "persistent", "temporary", NULL };
7208 return nmc_rl_gen_func_basic (text, state, words);
7212 gen_connection_types (const char *text, int state)
7214 static int list_idx, len;
7215 const char *c_type, *a_type;
7219 len = strlen (text);
7222 while (nmc_valid_connection_types[list_idx].name) {
7223 a_type = nmc_valid_connection_types[list_idx].alias;
7224 c_type = nmc_valid_connection_types[list_idx].name;
7226 if (a_type && !strncmp (text, a_type, len))
7227 return g_strdup (a_type);
7228 if (c_type && !strncmp (text, c_type, len))
7229 return g_strdup (c_type);
7236 gen_setting_names (const char *text, int state)
7238 static int list_idx, len, is_slv;
7239 const char *s_name, *a_name;
7240 const NameItem *valid_settings_arr;
7241 NMSettingConnection *s_con;
7242 const char *s_type = NULL;
7247 len = strlen (text);
7252 valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type);
7253 if (!valid_settings_arr)
7255 while (valid_settings_arr[list_idx].name) {
7256 a_name = valid_settings_arr[list_idx].alias;
7257 s_name = valid_settings_arr[list_idx].name;
7259 if (len == 0 && a_name)
7260 return g_strdup_printf ("%s (%s)", s_name, a_name);
7261 if (a_name && !strncmp (text, a_name, len))
7262 return g_strdup (a_name);
7263 if (s_name && !strncmp (text, s_name, len))
7264 return g_strdup (s_name);
7267 /* Let's give a try to parameters related to slave type */
7273 s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
7275 s_type = nm_setting_connection_get_slave_type (s_con);
7276 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
7277 valid_settings_arr = get_valid_settings_array (slv_type);
7280 while (valid_settings_arr[list_idx].name) {
7281 a_name = valid_settings_arr[list_idx].alias;
7282 s_name = valid_settings_arr[list_idx].name;
7284 if (len == 0 && a_name)
7285 return g_strdup_printf ("%s (%s)", s_name, a_name);
7286 if (a_name && !strncmp (text, a_name, len))
7287 return g_strdup (a_name);
7288 if (s_name && !strncmp (text, s_name, len))
7289 return g_strdup (s_name);
7296 gen_property_names (const char *text, int state)
7298 NMSetting *setting = NULL;
7299 char **valid_props = NULL;
7301 const char *line = rl_line_buffer;
7302 const char *setting_name;
7304 const NameItem *valid_settings_main;
7305 const NameItem *valid_settings_slave;
7307 const char *slv_type;
7309 /* Try to get the setting from 'line' - setting_name.property */
7310 p1 = strchr (line, '.');
7312 while (p1 > line && !g_ascii_isspace (*p1))
7315 strv = g_strsplit (p1+1, ".", 2);
7317 valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
7319 /* Support autocompletion of slave-connection parameters
7320 * guessing the slave type from the setting name already
7321 * typed (or autocompleted) */
7322 if (nm_streq0 (strv[0], NM_SETTING_TEAM_PORT_SETTING_NAME))
7323 slv_type = "team-slave";
7324 else if (nm_streq0 (strv[0], NM_SETTING_BRIDGE_PORT_SETTING_NAME))
7325 slv_type = "bridge-slave";
7327 slv_type = "no-slave";
7328 valid_settings_slave = get_valid_settings_array (slv_type);
7330 setting_name = check_valid_name (strv[0],
7331 valid_settings_main,
7332 valid_settings_slave,
7334 setting = nmc_setting_new_for_name (setting_name);
7336 /* Else take the current setting, if any */
7337 setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
7341 valid_props = nmc_setting_get_valid_properties (setting);
7342 ret = nmc_rl_gen_func_basic (text, state, (const char **) valid_props);
7346 g_strfreev (valid_props);
7348 g_object_unref (setting);
7353 gen_compat_devices (const char *text, int state)
7356 const GPtrArray *devices;
7357 const char **compatible_devices;
7360 devices = nm_client_get_devices (nmc_tab_completion.nmc->client);
7361 if (devices->len == 0)
7364 compatible_devices = g_new (const char *, devices->len + 1);
7365 for (i = 0; i < devices->len; i++) {
7366 NMDevice *dev = g_ptr_array_index (devices, i);
7367 const char *ifname = nm_device_get_iface (dev);
7368 NMDevice *device = NULL;
7369 const char *spec_object = NULL;
7371 if (find_device_for_connection (nmc_tab_completion.nmc, nmc_tab_completion.connection,
7372 ifname, NULL, NULL, &device, &spec_object, NULL)) {
7373 compatible_devices[j++] = ifname;
7376 compatible_devices[j] = NULL;
7378 ret = nmc_rl_gen_func_basic (text, state, compatible_devices);
7380 g_free (compatible_devices);
7384 static const char **
7385 _create_vpn_array (const GPtrArray *connections, gboolean uuid)
7390 if (connections->len < 1)
7393 array = g_new (const char *, connections->len + 1);
7394 for (c = 0; c < connections->len; c++) {
7395 NMConnection *connection = NM_CONNECTION (connections->pdata[c]);
7396 const char *type = nm_connection_get_connection_type (connection);
7398 if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) == 0)
7399 array[idx++] = uuid ? nm_connection_get_uuid (connection) : nm_connection_get_id (connection);
7406 gen_vpn_uuids (const char *text, int state)
7408 const GPtrArray *connections = nm_cli.connections;
7412 if (connections->len < 1)
7415 uuids = _create_vpn_array (connections, TRUE);
7416 ret = nmc_rl_gen_func_basic (text, state, uuids);
7422 gen_vpn_ids (const char *text, int state)
7424 const GPtrArray *connections = nm_cli.connections;
7428 if (connections->len < 1)
7431 ids = _create_vpn_array (connections, FALSE);
7432 ret = nmc_rl_gen_func_basic (text, state, ids);
7437 static rl_compentry_func_t *
7438 get_gen_func_cmd_nmcli (const char *str)
7442 if (matches (str, "status-line") == 0)
7443 return gen_func_bool_values;
7444 if (matches (str, "save-confirmation") == 0)
7445 return gen_func_bool_values;
7446 if (matches (str, "show-secrets") == 0)
7447 return gen_func_bool_values;
7448 if (matches (str, "prompt-color") == 0)
7449 return gen_cmd_nmcli_prompt_color;
7454 * Helper function parsing line for completion.
7456 * line : the whole line to be parsed
7457 * end : the position of cursor in the line
7458 * cmd : command to match
7460 * cw_num : is set to the word number being completed (1, 2, 3, 4).
7461 * prev_word : returns the previous word (so that we have some context).
7463 * Returns TRUE when the first word of the 'line' matches 'cmd'.
7466 * line="rem" cmd="remove" -> TRUE cw_num=1
7467 * line="set con" cmd="set" -> TRUE cw_num=2
7468 * line="go ipv4.method" cmd="goto" -> TRUE cw_num=2
7469 * line=" des eth.mtu " cmd="describe" -> TRUE cw_num=3
7470 * line=" bla ipv4.method" cmd="goto" -> FALSE
7473 should_complete_cmd (const char *line, int end, const char *cmd,
7474 int *cw_num, char **prev_word)
7477 const char *word1, *word2, *word3;
7478 size_t n1, n2, n3, n4, n5, n6;
7479 gboolean word1_done, word2_done, word3_done;
7480 gboolean ret = FALSE;
7485 tmp = g_strdup (line);
7487 n1 = strspn (tmp, " \t");
7488 n2 = strcspn (tmp+n1, " \t\0") + n1;
7489 n3 = strspn (tmp+n2, " \t") + n2;
7490 n4 = strcspn (tmp+n3, " \t\0") + n3;
7491 n5 = strspn (tmp+n4, " \t") + n4;
7492 n6 = strcspn (tmp+n5, " \t\0") + n5;
7494 word1_done = end > n2;
7495 word2_done = end > n4;
7496 word3_done = end > n6;
7497 tmp[n2] = tmp[n4] = tmp[n6] = '\0';
7499 word1 = tmp[n1] ? tmp + n1 : NULL;
7500 word2 = tmp[n3] ? tmp + n3 : NULL;
7501 word3 = tmp[n5] ? tmp + n5 : NULL;
7508 } else if (!word2_done) {
7512 *prev_word = g_strdup (word1);
7513 } else if (!word3_done) {
7517 *prev_word = g_strdup (word2);
7522 *prev_word = g_strdup (word3);
7525 if (word1 && matches (word1, cmd) == 0)
7533 * extract_setting_and_property:
7534 * prompt: (in) (allow-none): prompt string, or NULL
7535 * line: (in) (allow-none): line, or NULL
7536 * setting: (out) (transfer full) (array zero-terminated=1):
7537 * return location for setting name
7538 * property: (out) (transfer full) (array zero-terminated=1):
7539 * return location for property name
7541 * Extract setting and property names from prompt and/or line.
7544 extract_setting_and_property (const char *prompt, const char *line,
7545 char **setting, char **property)
7551 /* prompt looks like this:
7552 "nmcli 802-1x>" or "nmcli 802-1x.pac-file>" */
7553 const char *p1, *p2, *dot;
7555 p1 = strchr (prompt, ' ');
7557 dot = strchr (++p1, '.');
7560 num1 = strcspn (p1, ".");
7561 num2 = strcspn (p2, ">");
7562 sett = num1 > 0 ? g_strndup (p1, num1) : NULL;
7563 prop = num2 > 0 ? g_strndup (p2, num2) : NULL;
7565 num1 = strcspn (p1, ">");
7566 sett = num1 > 0 ? g_strndup (p1, num1) : NULL;
7572 /* line looks like this:
7573 " set 802-1x.pac-file ..." or " set pac-file ..." */
7574 const char *p1, *p2, *dot;
7575 size_t n1, n2, n3, n4;
7576 size_t num1, num2, len;
7577 n1 = strspn (line, " \t"); /* white-space */
7578 n2 = strcspn (line+n1, " \t\0") + n1; /* command */
7579 n3 = strspn (line+n2, " \t") + n2; /* white-space */
7580 n4 = strcspn (line+n3, " \t\0") + n3; /* setting/property */
7584 dot = strchr (p1, '.');
7585 if (dot && dot < p1 + len) {
7587 num1 = strcspn (p1, ".");
7588 num2 = len > num1 + 1 ? len - num1 - 1 : 0;
7589 sett = num1 > 0 ? g_strndup (p1, num1) : sett;
7590 prop = num2 > 0 ? g_strndup (p2, num2) : prop;
7593 prop = len > 0 ? g_strndup (p1, len) : NULL;
7608 get_setting_and_property (const char *prompt, const char *line,
7609 NMSetting **setting_out, char**property_out)
7611 const NameItem *valid_settings_main;
7612 const NameItem *valid_settings_slave;
7613 const char *setting_name;
7614 NMSetting *setting = NULL;
7615 char *property = NULL;
7616 char *sett = NULL, *prop = NULL;
7617 NMSettingConnection *s_con;
7618 const char *s_type = NULL;
7621 extract_setting_and_property (prompt, line, &sett, &prop);
7623 /* Is this too much (and useless?) effort for an unlikely case? */
7624 s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
7626 s_type = nm_setting_connection_get_slave_type (s_con);
7627 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
7629 valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
7630 valid_settings_slave = get_valid_settings_array (slv_type);
7633 setting_name = check_valid_name (sett, valid_settings_main,
7634 valid_settings_slave, NULL);
7635 setting = nmc_setting_new_for_name (setting_name);
7637 setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
7639 if (setting && prop)
7640 property = is_property_valid (setting, prop, NULL);
7642 property = g_strdup (nmc_tab_completion.property);
7644 *setting_out = setting;
7645 *property_out = property;
7652 _get_and_check_property (const char *prompt,
7655 const char **array_multi,
7659 gboolean found = FALSE;
7661 extract_setting_and_property (prompt, line, NULL, &prop);
7664 found = !!nmc_string_is_valid (prop, array, NULL);
7665 if (array_multi && multi)
7666 *multi = !!nmc_string_is_valid (prop, array_multi, NULL);
7673 should_complete_files (const char *prompt, const char *line)
7675 const char *file_properties[] = {
7676 /* '802-1x' properties */
7683 "phase2-client-cert",
7685 "phase2-private-key",
7686 /* 'team' and 'team-port' properties */
7690 return _get_and_check_property (prompt, line, file_properties, NULL, NULL);
7694 should_complete_vpn_uuids (const char *prompt, const char *line)
7696 const char *uuid_properties[] = {
7697 /* 'connection' properties */
7701 return _get_and_check_property (prompt, line, uuid_properties, NULL, NULL);
7704 static const char **
7705 get_allowed_property_values (void)
7709 const char **avals = NULL;
7711 get_setting_and_property (rl_prompt, rl_line_buffer, &setting, &property);
7712 if (setting && property)
7713 avals = nmc_setting_get_property_allowed_values (setting, property);
7716 g_object_unref (setting);
7723 should_complete_property_values (const char *prompt, const char *line, gboolean *multi)
7725 /* properties allowing multiple values */
7726 const char *multi_props[] = {
7727 /* '802-1x' properties */
7728 NM_SETTING_802_1X_EAP,
7729 /* '802-11-wireless-security' properties */
7730 NM_SETTING_WIRELESS_SECURITY_PROTO,
7731 NM_SETTING_WIRELESS_SECURITY_PAIRWISE,
7732 NM_SETTING_WIRELESS_SECURITY_GROUP,
7733 /* 'bond' properties */
7734 NM_SETTING_BOND_OPTIONS,
7735 /* 'ethernet' properties */
7736 NM_SETTING_WIRED_S390_OPTIONS,
7739 _get_and_check_property (prompt, line, NULL, multi_props, multi);
7740 return get_allowed_property_values () != NULL;
7743 //FIXME: this helper should go to libnm later
7745 _setting_property_is_boolean (NMSetting *setting, const char *property_name)
7749 g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
7750 g_return_val_if_fail (property_name, FALSE);
7752 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);
7753 if (pspec && pspec->value_type == G_TYPE_BOOLEAN)
7759 should_complete_boolean (const char *prompt, const char *line)
7763 gboolean is_boolean = FALSE;
7765 get_setting_and_property (prompt, line, &setting, &property);
7766 if (setting && property)
7767 is_boolean = _setting_property_is_boolean (setting, property);
7770 g_object_unref (setting);
7777 gen_property_values (const char *text, int state)
7782 avals = get_allowed_property_values ();
7784 ret = nmc_rl_gen_func_basic (text, state, avals);
7789 extern int rl_complete_with_tilde_expansion;
7792 * Attempt to complete on the contents of TEXT. START and END show the
7793 * region of TEXT that contains the word to complete. We can use the
7794 * entire line in case we want to do some simple parsing. Return the
7795 * array of matches, or NULL if there aren't any.
7798 nmcli_editor_tab_completion (const char *text, int start, int end)
7800 char **match_array = NULL;
7801 const char *line = rl_line_buffer;
7802 rl_compentry_func_t *generator_func = NULL;
7808 /* Restore standard append character to space */
7809 rl_completion_append_character = ' ';
7811 /* Restore standard function for displaying matches */
7812 rl_completion_display_matches_hook = NULL;
7814 /* Disable default filename completion */
7815 rl_attempted_completion_over = 1;
7817 /* Enable tilde expansion when filenames are completed */
7818 rl_complete_with_tilde_expansion = 1;
7820 /* Filter out possible ANSI color escape sequences */
7821 prompt_tmp = nmc_filter_out_colors ((const char *) rl_prompt);
7823 /* Find the first non-space character */
7824 n1 = strspn (line, " \t");
7826 /* Choose the right generator function */
7827 if (strcmp (prompt_tmp, EDITOR_PROMPT_CON_TYPE) == 0)
7828 generator_func = gen_connection_types;
7829 else if (strcmp (prompt_tmp, EDITOR_PROMPT_SETTING) == 0)
7830 generator_func = gen_setting_names;
7831 else if (strcmp (prompt_tmp, EDITOR_PROMPT_PROPERTY) == 0)
7832 generator_func = gen_property_names;
7833 else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL))
7834 || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL)))
7835 generator_func = gen_func_bool_values_l10n;
7836 else if (g_str_has_prefix (prompt_tmp, "nmcli")) {
7837 if (!strchr (prompt_tmp, '.')) {
7838 int level = g_str_has_prefix (prompt_tmp, "nmcli>") ? 0 : 1;
7839 const char *dot = strchr (line, '.');
7842 /* Main menu - level 0,1 */
7844 generator_func = gen_nmcli_cmds_menu;
7846 if (should_complete_cmd (line, end, "goto", &num, NULL) && num <= 2) {
7847 if (level == 0 && (!dot || dot >= line + end))
7848 generator_func = gen_setting_names;
7850 generator_func = gen_property_names;
7851 } else if (should_complete_cmd (line, end, "set", &num, NULL)) {
7853 if (level == 0 && (!dot || dot >= line + end)) {
7854 generator_func = gen_setting_names;
7855 rl_completion_append_character = '.';
7857 generator_func = gen_property_names;
7858 } else if (num >= 3) {
7859 if (num == 3 && should_complete_files (NULL, line))
7860 rl_attempted_completion_over = 0;
7861 else if (should_complete_vpn_uuids (NULL, line)) {
7862 rl_completion_display_matches_hook = uuid_display_hook;
7863 generator_func = gen_vpn_uuids;
7864 } else if ( should_complete_property_values (NULL, line, &multi)
7865 && (num == 3 || multi)) {
7866 generator_func = gen_property_values;
7867 } else if (should_complete_boolean (NULL, line) && num == 3)
7868 generator_func = gen_func_bool_values;
7870 } else if ( ( should_complete_cmd (line, end, "remove", &num, NULL)
7871 || should_complete_cmd (line, end, "describe", &num, NULL))
7873 if (level == 0 && (!dot || dot >= line + end)) {
7874 generator_func = gen_setting_names;
7875 rl_completion_append_character = '.';
7877 generator_func = gen_property_names;
7878 } else if (should_complete_cmd (line, end, "nmcli", &num, &word)) {
7880 generator_func = gen_cmd_nmcli;
7882 generator_func = get_gen_func_cmd_nmcli (word);
7883 } else if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2) {
7884 if (level == 0 && (!dot || dot >= line + end))
7885 generator_func = gen_cmd_print0;
7887 generator_func = gen_property_names;
7888 } else if (should_complete_cmd (line, end, "verify", &num, NULL) && num <= 2) {
7889 generator_func = gen_cmd_verify0;
7890 } else if (should_complete_cmd (line, end, "activate", &num, NULL) && num <= 2) {
7891 generator_func = gen_compat_devices;
7892 } else if (should_complete_cmd (line, end, "save", &num, NULL) && num <= 2) {
7893 generator_func = gen_cmd_save;
7894 } else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
7895 generator_func = gen_nmcli_cmds_menu;
7898 /* Submenu - level 2 */
7900 generator_func = gen_nmcli_cmds_submenu;
7904 if ( should_complete_cmd (line, end, "add", &num, NULL)
7905 || should_complete_cmd (line, end, "set", &num, NULL)) {
7906 if (num <= 2 && should_complete_files (prompt_tmp, line))
7907 rl_attempted_completion_over = 0;
7908 else if (should_complete_vpn_uuids (prompt_tmp, line)) {
7909 rl_completion_display_matches_hook = uuid_display_hook;
7910 generator_func = gen_vpn_uuids;
7911 } else if ( should_complete_property_values (prompt_tmp, NULL, &multi)
7912 && (num <= 2 || multi)) {
7913 generator_func = gen_property_values;
7914 } else if (should_complete_boolean (prompt_tmp, NULL) && num <= 2)
7915 generator_func = gen_func_bool_values;
7917 if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2)
7918 generator_func = gen_cmd_print2;
7919 else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
7920 generator_func = gen_nmcli_cmds_submenu;
7926 match_array = rl_completion_matches (text, generator_func);
7928 g_free (prompt_tmp);
7933 #define NMCLI_EDITOR_HISTORY ".nmcli-history"
7936 load_history_cmds (const char *uuid)
7945 filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
7946 kf = g_key_file_new ();
7947 if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
7948 if (g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE))
7949 g_print ("Warning: %s parse error: %s\n", filename, err->message);
7950 g_key_file_free (kf);
7954 keys = g_key_file_get_keys (kf, uuid, NULL, NULL);
7955 for (i = 0; keys && keys[i]; i++) {
7956 line = g_key_file_get_string (kf, uuid, keys[i], NULL);
7962 g_key_file_free (kf);
7967 save_history_cmds (const char *uuid)
7969 HIST_ENTRY **hist = NULL;
7978 hist = history_list ();
7980 filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
7981 kf = g_key_file_new ();
7982 if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
7983 if ( !g_error_matches (err, G_FILE_ERROR, G_FILE_ERROR_NOENT)
7984 && !g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
7985 g_print ("Warning: %s parse error: %s\n", filename, err->message);
7986 g_key_file_free (kf);
7988 g_clear_error (&err);
7991 g_clear_error (&err);
7994 /* Remove previous history group and save new history entries */
7995 g_key_file_remove_group (kf, uuid, NULL);
7996 for (i = 0; hist[i]; i++)
7998 key = g_strdup_printf ("%zd", i);
7999 g_key_file_set_string (kf, uuid, key, hist[i]->line);
8003 /* Write history to file */
8004 data = g_key_file_to_data (kf, &len, NULL);
8006 g_file_set_contents (filename, data, len, NULL);
8009 g_key_file_free (kf);
8014 /*----------------------------------------------------------------------------*/
8017 editor_show_connection (NMConnection *connection, NmCli *nmc)
8019 nmc->print_output = NMC_PRINT_PRETTY;
8020 nmc->multiline_output = TRUE;
8021 nmc->escape_values = 0;
8023 /* Remove any previous data */
8024 nmc_empty_output_fields (nmc);
8026 nmc_connection_profile_details (connection, nmc, nmc->editor_show_secrets);
8030 editor_show_setting (NMSetting *setting, NmCli *nmc)
8032 g_print (_("['%s' setting values]\n"),
8033 nm_setting_get_name (setting));
8035 nmc->print_output = NMC_PRINT_NORMAL;
8036 nmc->multiline_output = TRUE;
8037 nmc->escape_values = 0;
8039 /* Remove any previous data */
8040 nmc_empty_output_fields (nmc);
8042 setting_details (setting, nmc, NULL, nmc->editor_show_secrets);
8046 NMC_EDITOR_MAIN_CMD_UNKNOWN = 0,
8047 NMC_EDITOR_MAIN_CMD_GOTO,
8048 NMC_EDITOR_MAIN_CMD_REMOVE,
8049 NMC_EDITOR_MAIN_CMD_SET,
8050 NMC_EDITOR_MAIN_CMD_DESCRIBE,
8051 NMC_EDITOR_MAIN_CMD_PRINT,
8052 NMC_EDITOR_MAIN_CMD_VERIFY,
8053 NMC_EDITOR_MAIN_CMD_SAVE,
8054 NMC_EDITOR_MAIN_CMD_ACTIVATE,
8055 NMC_EDITOR_MAIN_CMD_BACK,
8056 NMC_EDITOR_MAIN_CMD_HELP,
8057 NMC_EDITOR_MAIN_CMD_NMCLI,
8058 NMC_EDITOR_MAIN_CMD_QUIT,
8061 static NmcEditorMainCmd
8062 parse_editor_main_cmd (const char *cmd, char **cmd_arg)
8064 NmcEditorMainCmd editor_cmd = NMC_EDITOR_MAIN_CMD_UNKNOWN;
8067 vec = nmc_strsplit_set (cmd, " \t", 2);
8068 if (g_strv_length (vec) < 1) {
8071 return NMC_EDITOR_MAIN_CMD_UNKNOWN;
8074 if (matches (vec[0], "goto") == 0)
8075 editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO;
8076 else if (matches (vec[0], "remove") == 0)
8077 editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE;
8078 else if (matches (vec[0], "set") == 0)
8079 editor_cmd = NMC_EDITOR_MAIN_CMD_SET;
8080 else if (matches (vec[0], "describe") == 0)
8081 editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE;
8082 else if (matches (vec[0], "print") == 0)
8083 editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT;
8084 else if (matches (vec[0], "verify") == 0)
8085 editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY;
8086 else if (matches (vec[0], "save") == 0)
8087 editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE;
8088 else if (matches (vec[0], "activate") == 0)
8089 editor_cmd = NMC_EDITOR_MAIN_CMD_ACTIVATE;
8090 else if (matches (vec[0], "back") == 0)
8091 editor_cmd = NMC_EDITOR_MAIN_CMD_BACK;
8092 else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
8093 editor_cmd = NMC_EDITOR_MAIN_CMD_HELP;
8094 else if (matches (vec[0], "quit") == 0)
8095 editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT;
8096 else if (matches (vec[0], "nmcli") == 0)
8097 editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI;
8099 /* set pointer to command argument */
8101 *cmd_arg = vec[1] ? g_strstrip (g_strdup (vec[1])) : NULL;
8108 editor_main_usage (void)
8110 g_print ("------------------------------------------------------------------------------\n");
8111 /* TRANSLATORS: do not translate command names and keywords before ::
8112 * However, you should translate terms enclosed in <>.
8114 g_print (_("---[ Main menu ]---\n"
8115 "goto [<setting> | <prop>] :: go to a setting or property\n"
8116 "remove <setting>[.<prop>] | <prop> :: remove setting or reset property value\n"
8117 "set [<setting>.<prop> <value>] :: set property value\n"
8118 "describe [<setting>.<prop>] :: describe property\n"
8119 "print [all | <setting>[.<prop>]] :: print the connection\n"
8120 "verify [all | fix] :: verify the connection\n"
8121 "save [persistent|temporary] :: save the connection\n"
8122 "activate [<ifname>] [/<ap>|<nsp>] :: activate the connection\n"
8123 "back :: go one level up (back)\n"
8124 "help/? [<command>] :: print this help\n"
8125 "nmcli <conf-option> <value> :: nmcli configuration\n"
8126 "quit :: exit nmcli\n"));
8127 g_print ("------------------------------------------------------------------------------\n");
8131 editor_main_help (const char *command)
8134 editor_main_usage ();
8136 /* detailed command descriptions */
8137 NmcEditorMainCmd cmd = parse_editor_main_cmd (command, NULL);
8140 case NMC_EDITOR_MAIN_CMD_GOTO:
8141 g_print (_("goto <setting>[.<prop>] | <prop> :: enter setting/property for editing\n\n"
8142 "This command enters into a setting or property for editing it.\n\n"
8143 "Examples: nmcli> goto connection\n"
8144 " nmcli connection> goto secondaries\n"
8145 " nmcli> goto ipv4.addresses\n"));
8147 case NMC_EDITOR_MAIN_CMD_REMOVE:
8148 g_print (_("remove <setting>[.<prop>] :: remove setting or reset property value\n\n"
8149 "This command removes an entire setting from the connection, or if a property\n"
8150 "is given, resets that property to the default value.\n\n"
8151 "Examples: nmcli> remove wifi-sec\n"
8152 " nmcli> remove eth.mtu\n"));
8154 case NMC_EDITOR_MAIN_CMD_SET:
8155 g_print (_("set [<setting>.<prop> <value>] :: set property value\n\n"
8156 "This command sets property value.\n\n"
8157 "Example: nmcli> set con.id My connection\n"));
8159 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
8160 g_print (_("describe [<setting>.<prop>] :: describe property\n\n"
8161 "Shows property description. You can consult nm-settings(5) "
8162 "manual page to see all NM settings and properties.\n"));
8164 case NMC_EDITOR_MAIN_CMD_PRINT:
8165 g_print (_("print [all] :: print setting or connection values\n\n"
8166 "Shows current property or the whole connection.\n\n"
8167 "Example: nmcli ipv4> print all\n"));
8169 case NMC_EDITOR_MAIN_CMD_VERIFY:
8170 g_print (_("verify [all | fix] :: verify setting or connection validity\n\n"
8171 "Verifies whether the setting or connection is valid and can be saved later.\n"
8172 "It indicates invalid values on error. Some errors may be fixed automatically\n"
8173 "by 'fix' option.\n\n"
8174 "Examples: nmcli> verify\n"
8175 " nmcli> verify fix\n"
8176 " nmcli bond> verify\n"));
8178 case NMC_EDITOR_MAIN_CMD_SAVE:
8179 g_print (_("save [persistent|temporary] :: save the connection\n\n"
8180 "Sends the connection profile to NetworkManager that either will save it\n"
8181 "persistently, or will only keep it in memory. 'save' without an argument\n"
8182 "means 'save persistent'.\n"
8183 "Note that once you save the profile persistently those settings are saved\n"
8184 "across reboot or restart. Subsequent changes can also be temporary or\n"
8185 "persistent, but any temporary changes will not persist across reboot or\n"
8186 "restart. If you want to fully remove the persistent connection, the connection\n"
8187 "profile must be deleted.\n"));
8189 case NMC_EDITOR_MAIN_CMD_ACTIVATE:
8190 g_print (_("activate [<ifname>] [/<ap>|<nsp>] :: activate the connection\n\n"
8191 "Activates the connection.\n\n"
8192 "Available options:\n"
8193 "<ifname> - device the connection will be activated on\n"
8194 "/<ap>|<nsp> - AP (Wi-Fi) or NSP (WiMAX) (prepend with / when <ifname> is not specified)\n"));
8196 case NMC_EDITOR_MAIN_CMD_BACK:
8197 g_print (_("back :: go to upper menu level\n\n"));
8199 case NMC_EDITOR_MAIN_CMD_HELP:
8200 g_print (_("help/? [<command>] :: help for the nmcli commands\n\n"));
8202 case NMC_EDITOR_MAIN_CMD_NMCLI:
8203 g_print (_("nmcli [<conf-option> <value>] :: nmcli configuration\n\n"
8204 "Configures nmcli. The following options are available:\n"
8205 "status-line yes | no [default: no]\n"
8206 "save-confirmation yes | no [default: yes]\n"
8207 "show-secrets yes | no [default: no]\n"
8208 "prompt-color <color> | <0-8> [default: 0]\n"
8209 "%s" /* color table description */
8211 "Examples: nmcli> nmcli status-line yes\n"
8212 " nmcli> nmcli save-confirmation no\n"
8213 " nmcli> nmcli prompt-color 3\n"),
8215 " 1 = \33[30mblack\33[0m\n"
8216 " 2 = \33[31mred\33[0m\n"
8217 " 3 = \33[32mgreen\33[0m\n"
8218 " 4 = \33[33myellow\33[0m\n"
8219 " 5 = \33[34mblue\33[0m\n"
8220 " 6 = \33[35mmagenta\33[0m\n"
8221 " 7 = \33[36mcyan\33[0m\n"
8222 " 8 = \33[37mwhite\33[0m\n");
8224 case NMC_EDITOR_MAIN_CMD_QUIT:
8225 g_print (_("quit :: exit nmcli\n\n"
8226 "This command exits nmcli. When the connection being edited "
8227 "is not saved, the user is asked to confirm the action.\n"));
8230 g_print (_("Unknown command: '%s'\n"), command);
8237 NMC_EDITOR_SUB_CMD_UNKNOWN = 0,
8238 NMC_EDITOR_SUB_CMD_SET,
8239 NMC_EDITOR_SUB_CMD_ADD,
8240 NMC_EDITOR_SUB_CMD_CHANGE,
8241 NMC_EDITOR_SUB_CMD_REMOVE,
8242 NMC_EDITOR_SUB_CMD_DESCRIBE,
8243 NMC_EDITOR_SUB_CMD_PRINT,
8244 NMC_EDITOR_SUB_CMD_BACK,
8245 NMC_EDITOR_SUB_CMD_HELP,
8246 NMC_EDITOR_SUB_CMD_QUIT
8249 static NmcEditorSubCmd
8250 parse_editor_sub_cmd (const char *cmd, char **cmd_arg)
8252 NmcEditorSubCmd editor_cmd = NMC_EDITOR_SUB_CMD_UNKNOWN;
8255 vec = nmc_strsplit_set (cmd, " \t", 2);
8256 if (g_strv_length (vec) < 1) {
8259 return NMC_EDITOR_SUB_CMD_UNKNOWN;
8262 if (matches (vec[0], "set") == 0)
8263 editor_cmd = NMC_EDITOR_SUB_CMD_SET;
8264 else if (matches (vec[0], "add") == 0)
8265 editor_cmd = NMC_EDITOR_SUB_CMD_ADD;
8266 else if (matches (vec[0], "change") == 0)
8267 editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE;
8268 else if (matches (vec[0], "remove") == 0)
8269 editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE;
8270 else if (matches (vec[0], "describe") == 0)
8271 editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE;
8272 else if (matches (vec[0], "print") == 0)
8273 editor_cmd = NMC_EDITOR_SUB_CMD_PRINT;
8274 else if (matches (vec[0], "back") == 0)
8275 editor_cmd = NMC_EDITOR_SUB_CMD_BACK;
8276 else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
8277 editor_cmd = NMC_EDITOR_SUB_CMD_HELP;
8278 else if (matches (vec[0], "quit") == 0)
8279 editor_cmd = NMC_EDITOR_SUB_CMD_QUIT;
8281 /* set pointer to command argument */
8283 *cmd_arg = g_strdup (vec[1]);
8290 editor_sub_help (void)
8292 g_print ("------------------------------------------------------------------------------\n");
8293 /* TRANSLATORS: do not translate command names and keywords before ::
8294 * However, you should translate terms enclosed in <>.
8296 g_print (_("---[ Property menu ]---\n"
8297 "set [<value>] :: set new value\n"
8298 "add [<value>] :: add new option to the property\n"
8299 "change :: change current value\n"
8300 "remove [<index> | <option>] :: delete the value\n"
8301 "describe :: describe property\n"
8302 "print [setting | connection] :: print property (setting/connection) value(s)\n"
8303 "back :: go to upper level\n"
8304 "help/? [<command>] :: print this help or command description\n"
8305 "quit :: exit nmcli\n"));
8306 g_print ("------------------------------------------------------------------------------\n");
8310 editor_sub_usage (const char *command)
8316 /* detailed command descriptions */
8317 NmcEditorSubCmd cmdsub = parse_editor_sub_cmd (command, NULL);
8320 case NMC_EDITOR_SUB_CMD_SET:
8321 g_print (_("set [<value>] :: set new value\n\n"
8322 "This command sets provided <value> to this property\n"));
8324 case NMC_EDITOR_SUB_CMD_ADD:
8325 g_print (_("add [<value>] :: append new value to the property\n\n"
8326 "This command adds provided <value> to this property, if "
8327 "the property is of a container type. For single-valued "
8328 "properties the property value is replaced (same as 'set').\n"));
8330 case NMC_EDITOR_SUB_CMD_CHANGE:
8331 g_print (_("change :: change current value\n\n"
8332 "Displays current value and allows editing it.\n"));
8334 case NMC_EDITOR_SUB_CMD_REMOVE:
8335 g_print (_("remove [<value>|<index>|<option name>] :: delete the value\n\n"
8336 "Removes the property value. For single-valued properties, this sets the\n"
8337 "property back to its default value. For container-type properties, this removes\n"
8338 "all the values of that property, or you can specify an argument to remove just\n"
8339 "a single item or option. The argument is either a value or index of the item to\n"
8340 "remove, or an option name (for properties with named options).\n\n"
8341 "Examples: nmcli ipv4.dns> remove 8.8.8.8\n"
8342 " nmcli ipv4.dns> remove 2\n"
8343 " nmcli bond.options> remove downdelay\n\n"));
8345 case NMC_EDITOR_SUB_CMD_DESCRIBE:
8346 g_print (_("describe :: describe property\n\n"
8347 "Shows property description. You can consult nm-settings(5) "
8348 "manual page to see all NM settings and properties.\n"));
8350 case NMC_EDITOR_SUB_CMD_PRINT:
8351 g_print (_("print [property|setting|connection] :: print property (setting, connection) value(s)\n\n"
8352 "Shows property value. Providing an argument you can also display "
8353 "values for the whole setting or connection.\n"));
8355 case NMC_EDITOR_SUB_CMD_BACK:
8356 g_print (_("back :: go to upper menu level\n\n"));
8358 case NMC_EDITOR_SUB_CMD_HELP:
8359 g_print (_("help/? [<command>] :: help for nmcli commands\n\n"));
8361 case NMC_EDITOR_SUB_CMD_QUIT:
8362 g_print (_("quit :: exit nmcli\n\n"
8363 "This command exits nmcli. When the connection being edited "
8364 "is not saved, the user is asked to confirm the action.\n"));
8367 g_print (_("Unknown command: '%s'\n"), command);
8373 /*----------------------------------------------------------------------------*/
8377 NMActiveConnection *ac;
8381 static gboolean nmc_editor_cb_called;
8382 static GError *nmc_editor_error;
8383 static MonitorACInfo *nmc_editor_monitor_ac;
8384 static GMutex nmc_editor_mutex;
8385 static GCond nmc_editor_cond;
8388 * Store 'error' to shared 'nmc_editor_error' and monitoring info to
8389 * 'nmc_editor_monitor_ac' and signal the condition so that
8390 * the 'editor-thread' thread could process that.
8393 set_info_and_signal_editor_thread (GError *error, MonitorACInfo *monitor_ac_info)
8395 g_mutex_lock (&nmc_editor_mutex);
8396 nmc_editor_cb_called = TRUE;
8397 nmc_editor_error = error ? g_error_copy (error) : NULL;
8398 nmc_editor_monitor_ac = monitor_ac_info;
8399 g_cond_signal (&nmc_editor_cond);
8400 g_mutex_unlock (&nmc_editor_mutex);
8404 add_connection_editor_cb (GObject *client,
8405 GAsyncResult *result,
8408 NMRemoteConnection *connection;
8409 GError *error = NULL;
8411 connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
8412 set_info_and_signal_editor_thread (error, NULL);
8414 g_clear_object (&connection);
8415 g_clear_error (&error);
8419 update_connection_editor_cb (GObject *connection,
8420 GAsyncResult *result,
8423 GError *error = NULL;
8425 nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
8427 set_info_and_signal_editor_thread (error, NULL);
8428 g_clear_error (&error);
8432 progress_activation_editor_cb (gpointer user_data)
8434 MonitorACInfo *info = (MonitorACInfo *) user_data;
8435 NMDevice *device = info->device;
8436 NMActiveConnection *ac = info->ac;
8437 NMActiveConnectionState ac_state;
8438 NMDeviceState dev_state;
8443 ac_state = nm_active_connection_get_state (ac);
8444 dev_state = nm_device_get_state (device);
8446 nmc_terminal_show_progress (nmc_device_state_to_string (dev_state));
8448 if ( ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED
8449 || dev_state == NM_DEVICE_STATE_ACTIVATED) {
8450 nmc_terminal_erase_line ();
8451 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
8452 nm_object_get_path (NM_OBJECT (ac)));
8453 goto finish; /* we are done */
8454 } else if ( ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED
8455 || dev_state == NM_DEVICE_STATE_FAILED) {
8456 nmc_terminal_erase_line ();
8457 g_print (_("Error: Connection activation failed.\n"));
8458 goto finish; /* we are done */
8465 g_object_unref (device);
8467 g_object_unref (ac);
8472 activate_connection_editor_cb (GObject *client,
8473 GAsyncResult *result,
8476 ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
8477 NMDevice *device = info->device;
8478 const GPtrArray *ac_devs;
8479 MonitorACInfo *monitor_ac_info = NULL;
8480 NMActiveConnection *active;
8481 GError *error = NULL;
8483 active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
8487 ac_devs = nm_active_connection_get_devices (active);
8488 device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
8491 monitor_ac_info = g_malloc0 (sizeof (AddConnectionInfo));
8492 monitor_ac_info->device = g_object_ref (device);
8493 monitor_ac_info->ac = active;
8494 monitor_ac_info->monitor_id = g_timeout_add (120, progress_activation_editor_cb, monitor_ac_info);
8496 g_object_unref (active);
8498 set_info_and_signal_editor_thread (error, monitor_ac_info);
8499 g_clear_error (&error);
8502 /*----------------------------------------------------------------------------*/
8505 print_property_description (NMSetting *setting, const char *prop_name)
8509 desc = nmc_setting_get_property_desc (setting, prop_name);
8510 g_print ("\n=== [%s] ===\n%s\n", prop_name, desc);
8515 print_setting_description (NMSetting *setting)
8517 /* Show description of all properties */
8521 all_props = nmc_setting_get_valid_properties (setting);
8522 g_print (("<<< %s >>>\n"), nm_setting_get_name (setting));
8523 for (i = 0; all_props && all_props[i]; i++)
8524 print_property_description (setting, all_props[i]);
8525 g_strfreev (all_props);
8529 connection_remove_setting (NMConnection *connection, NMSetting *setting)
8533 g_return_val_if_fail (setting, FALSE);
8535 mandatory = is_setting_mandatory (connection, setting);
8537 nm_connection_remove_setting (connection, G_OBJECT_TYPE (setting));
8540 g_print (_("Error: setting '%s' is mandatory and cannot be removed.\n"),
8541 nm_setting_get_name (setting));
8546 editor_show_status_line (NMConnection *connection, gboolean dirty, gboolean temp)
8548 NMSettingConnection *s_con;
8549 const char *con_type, *con_id, *con_uuid;
8551 s_con = nm_connection_get_setting_connection (connection);
8553 con_type = nm_setting_connection_get_connection_type (s_con);
8554 con_id = nm_connection_get_id (connection);
8555 con_uuid = nm_connection_get_uuid (connection);
8557 /* TRANSLATORS: status line in nmcli connection editor */
8558 g_print (_("[ Type: %s | Name: %s | UUID: %s | Dirty: %s | Temp: %s ]\n"),
8559 con_type, con_id, con_uuid,
8560 dirty ? _("yes") : _("no"),
8561 temp ? _("yes") : _("no"));
8565 refresh_remote_connection (GWeakRef *weak, NMRemoteConnection **remote)
8569 g_return_val_if_fail (remote != NULL, FALSE);
8571 previous = (*remote != NULL);
8573 g_object_unref (*remote);
8574 *remote = g_weak_ref_get (weak);
8576 return (previous && !*remote);
8580 is_connection_dirty (NMConnection *connection, NMRemoteConnection *remote)
8582 return !nm_connection_compare (connection,
8583 remote ? NM_CONNECTION (remote) : NULL,
8584 NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS |
8585 NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP);
8592 gboolean want_quit = FALSE;
8594 answer = nmc_readline (_("The connection is not saved. "
8595 "Do you really want to quit? %s"),
8596 prompt_yes_no (FALSE, NULL));
8597 answer = answer ? g_strstrip (answer) : NULL;
8598 if (answer && matches (answer, WORD_LOC_YES) == 0)
8606 * Submenu for detailed property editing
8607 * Return: TRUE - continue; FALSE - should quit
8610 property_edit_submenu (NmCli *nmc,
8611 NMConnection *connection,
8612 NMRemoteConnection **rem_con,
8613 GWeakRef *rem_con_weak,
8614 NMSetting *curr_setting,
8615 const char *prop_name)
8617 NmcEditorSubCmd cmdsub;
8618 gboolean cmd_property_loop = TRUE;
8619 gboolean should_quit = FALSE;
8620 char *prop_val_user;
8621 gboolean set_result;
8622 GError *tmp_err = NULL;
8625 GValue prop_g_value = G_VALUE_INIT;
8626 gboolean temp_changes;
8629 /* Set global variable for use in TAB completion */
8630 nmc_tab_completion.property = prop_name;
8632 prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
8634 nm_setting_get_name (curr_setting), prop_name);
8636 while (cmd_property_loop) {
8637 char *cmd_property_user;
8638 char *cmd_property_arg;
8640 /* Get the remote connection again, it may have disapeared */
8641 removed = refresh_remote_connection (rem_con_weak, rem_con);
8643 g_print (_("The connection profile has been removed from another client. "
8644 "You may type 'save' in the main menu to restore it.\n"));
8646 /* Connection is dirty? (not saved or differs from the saved) */
8647 dirty = is_connection_dirty (connection, *rem_con);
8648 temp_changes = *rem_con ? nm_remote_connection_get_unsaved (*rem_con) : TRUE;
8649 if (nmc->editor_status_line)
8650 editor_show_status_line (connection, dirty, temp_changes);
8652 cmd_property_user = nmc_readline ("%s", prompt);
8653 if (!cmd_property_user || *cmd_property_user == '\0')
8655 cmdsub = parse_editor_sub_cmd (g_strstrip (cmd_property_user), &cmd_property_arg);
8658 case NMC_EDITOR_SUB_CMD_SET:
8659 case NMC_EDITOR_SUB_CMD_ADD:
8660 /* list, arrays,...: SET replaces the whole property value
8661 * ADD adds the new value(s)
8662 * single values: : both SET and ADD sets the new value
8664 if (!cmd_property_arg) {
8665 const char **avals = nmc_setting_get_property_allowed_values (curr_setting, prop_name);
8667 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
8668 g_print (_("Allowed values for '%s' property: %s\n"),
8669 prop_name, avals_str);
8672 prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
8674 prop_val_user = g_strdup (cmd_property_arg);
8676 /* nmc_setting_set_property() only adds new value, thus we have to
8677 * remove the original value and save it for error cases.
8679 if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
8680 nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
8681 nmc_property_set_default_value (curr_setting, prop_name);
8684 set_result = nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err);
8685 g_free (prop_val_user);
8687 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
8688 g_clear_error (&tmp_err);
8689 if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
8690 /* Block change signals and restore original value */
8691 g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8692 nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
8693 g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8696 if (G_IS_VALUE (&prop_g_value))
8697 g_value_unset (&prop_g_value);
8700 case NMC_EDITOR_SUB_CMD_CHANGE:
8701 rl_startup_hook = nmc_rl_set_deftext;
8702 nmc_rl_pre_input_deftext = nmc_setting_get_property_parsable (curr_setting, prop_name, NULL);
8703 prop_val_user = nmc_readline (_("Edit '%s' value: "), prop_name);
8705 nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
8706 nmc_property_set_default_value (curr_setting, prop_name);
8708 if (!nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err)) {
8709 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
8710 g_clear_error (&tmp_err);
8711 g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8712 nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
8713 g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8715 g_free (prop_val_user);
8716 if (G_IS_VALUE (&prop_g_value))
8717 g_value_unset (&prop_g_value);
8720 case NMC_EDITOR_SUB_CMD_REMOVE:
8721 if (cmd_property_arg) {
8722 unsigned long val_int = G_MAXUINT32;
8723 char *option = NULL;
8725 if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int))
8726 option = g_strdup (cmd_property_arg);
8728 if (!nmc_setting_remove_property_option (curr_setting, prop_name,
8729 option ? g_strstrip (option) : NULL,
8732 g_print (_("Error: %s\n"), tmp_err->message);
8733 g_clear_error (&tmp_err);
8737 if (!nmc_setting_reset_property (curr_setting, prop_name, &tmp_err)) {
8738 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
8740 g_clear_error (&tmp_err);
8745 case NMC_EDITOR_SUB_CMD_DESCRIBE:
8746 /* Show property description */
8747 print_property_description (curr_setting, prop_name);
8750 case NMC_EDITOR_SUB_CMD_PRINT:
8751 /* Print current connection settings/properties */
8752 if (cmd_property_arg) {
8753 if (matches (cmd_property_arg, "setting") == 0)
8754 editor_show_setting (curr_setting, nmc);
8755 else if ( matches (cmd_property_arg, "connection") == 0
8756 || matches (cmd_property_arg, "all") == 0)
8757 editor_show_connection (connection, nmc);
8759 g_print (_("Unknown command argument: '%s'\n"), cmd_property_arg);
8761 char *prop_val = nmc_setting_get_property (curr_setting, prop_name, NULL);
8762 g_print ("%s: %s\n", prop_name, prop_val);
8767 case NMC_EDITOR_SUB_CMD_BACK:
8768 /* Set global variable for use in TAB completion */
8769 nmc_tab_completion.property = NULL;
8770 cmd_property_loop = FALSE;
8773 case NMC_EDITOR_SUB_CMD_HELP:
8774 editor_sub_usage (cmd_property_arg);
8777 case NMC_EDITOR_SUB_CMD_QUIT:
8778 if (is_connection_dirty (connection, *rem_con)) {
8779 if (confirm_quit ()) {
8780 cmd_property_loop = FALSE;
8781 should_quit = TRUE; /* we will quit nmcli */
8784 cmd_property_loop = FALSE;
8785 should_quit = TRUE; /* we will quit nmcli */
8789 case NMC_EDITOR_SUB_CMD_UNKNOWN:
8791 g_print (_("Unknown command: '%s'\n"), cmd_property_user);
8794 g_free (cmd_property_user);
8795 g_free (cmd_property_arg);
8799 return !should_quit;
8803 * Split 'str' in the following format: [[[setting.]property] [value]]
8804 * and return the components in 'setting', 'property' and 'value'
8805 * Use g_free() to deallocate the returned strings.
8808 split_editor_main_cmd_args (const char *str, char **setting, char **property, char **value)
8810 char **args, **items;
8815 args = nmc_strsplit_set (str, " \t", 2);
8817 items = nmc_strsplit_set (args[0], ".", 2);
8818 if (g_strv_length (items) == 2) {
8820 *setting = g_strdup (items[0]);
8822 *property = g_strdup (items[1]);
8825 *property = g_strdup (items[0]);
8829 if (value && args[1])
8830 *value = g_strstrip (g_strdup (args[1]));
8836 create_setting_by_name (const char *name, const NameItem *valid_settings_main, const NameItem *valid_settings_slave)
8838 const char *setting_name;
8839 NMSetting *setting = NULL;
8841 /* Get a valid setting name */
8842 setting_name = check_valid_name (name, valid_settings_main, valid_settings_slave, NULL);
8845 setting = nmc_setting_new_for_name (setting_name);
8847 return NULL; /* This should really not happen */
8848 nmc_setting_custom_init (setting);
8854 ask_check_setting (const char *arg,
8855 const NameItem *valid_settings_main,
8856 const NameItem *valid_settings_slave,
8857 const char *valid_settings_str)
8859 char *setting_name_user;
8860 const char *setting_name;
8864 g_print (_("Available settings: %s\n"), valid_settings_str);
8865 setting_name_user = nmc_readline (EDITOR_PROMPT_SETTING);
8867 setting_name_user = g_strdup (arg);
8869 if (setting_name_user)
8870 g_strstrip (setting_name_user);
8872 if (!(setting_name = check_valid_name (setting_name_user,
8873 valid_settings_main,
8874 valid_settings_slave,
8876 g_print (_("Error: invalid setting name; %s\n"), err->message);
8877 g_clear_error (&err);
8879 g_free (setting_name_user);
8880 return setting_name;
8884 ask_check_property (const char *arg,
8885 const char **valid_props,
8886 const char *valid_props_str)
8888 char *prop_name_user;
8889 const char *prop_name;
8890 GError *tmp_err = NULL;
8893 g_print (_("Available properties: %s\n"), valid_props_str);
8894 prop_name_user = nmc_readline (EDITOR_PROMPT_PROPERTY);
8896 g_strstrip (prop_name_user);
8898 prop_name_user = g_strdup (arg);
8900 if (!(prop_name = nmc_string_is_valid (prop_name_user, valid_props, &tmp_err))) {
8901 g_print (_("Error: property %s\n"), tmp_err->message);
8902 g_clear_error (&tmp_err);
8904 g_free (prop_name_user);
8908 /* Copy timestamp from src do dst */
8910 update_connection_timestamp (NMConnection *src, NMConnection *dst)
8912 NMSettingConnection *s_con_src, *s_con_dst;
8914 s_con_src = nm_connection_get_setting_connection (src);
8915 s_con_dst = nm_connection_get_setting_connection (dst);
8916 if (s_con_src && s_con_dst) {
8917 guint64 timestamp = nm_setting_connection_get_timestamp (s_con_src);
8918 g_object_set (s_con_dst, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL);
8923 confirm_connection_saving (NMConnection *local, NMConnection *remote)
8925 NMSettingConnection *s_con_loc, *s_con_rem;
8926 gboolean ac_local, ac_remote;
8927 gboolean confirmed = TRUE;
8929 s_con_loc = nm_connection_get_setting_connection (local);
8930 g_assert (s_con_loc);
8931 ac_local = nm_setting_connection_get_autoconnect (s_con_loc);
8934 s_con_rem = nm_connection_get_setting_connection (remote);
8935 g_assert (s_con_rem);
8936 ac_remote = nm_setting_connection_get_autoconnect (s_con_rem);
8940 if (ac_local && !ac_remote) {
8942 answer = nmc_readline (_("Saving the connection with 'autoconnect=yes'. "
8943 "That might result in an immediate activation of the connection.\n"
8944 "Do you still want to save? %s"), prompt_yes_no (TRUE, NULL));
8945 answer = answer ? g_strstrip (answer) : NULL;
8946 if (!answer || matches (answer, WORD_LOC_YES) == 0)
8958 NMSetting *curr_setting;
8960 char *valid_props_str;
8961 } NmcEditorMenuContext;
8964 menu_switch_to_level0 (NmCli *nmc,
8965 NmcEditorMenuContext *menu_ctx,
8967 NmcTermColor prompt_color)
8969 menu_ctx->level = 0;
8970 g_free (menu_ctx->main_prompt);
8971 menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL, "%s", prompt);
8972 menu_ctx->curr_setting = NULL;
8973 g_strfreev (menu_ctx->valid_props);
8974 menu_ctx->valid_props = NULL;
8975 g_free (menu_ctx->valid_props_str);
8976 menu_ctx->valid_props_str = NULL;
8980 menu_switch_to_level1 (NmCli *nmc,
8981 NmcEditorMenuContext *menu_ctx,
8983 const char *setting_name,
8984 NmcTermColor prompt_color)
8986 menu_ctx->level = 1;
8987 g_free (menu_ctx->main_prompt);
8988 menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL,
8989 "nmcli %s> ", setting_name);
8990 menu_ctx->curr_setting = setting;
8991 g_strfreev (menu_ctx->valid_props);
8992 menu_ctx->valid_props = nmc_setting_get_valid_properties (menu_ctx->curr_setting);
8993 g_free (menu_ctx->valid_props_str);
8994 menu_ctx->valid_props_str = g_strjoinv (", ", menu_ctx->valid_props);
8998 editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type)
9000 NMSettingConnection *s_con;
9001 NMRemoteConnection *rem_con;
9002 NMRemoteConnection *con_tmp;
9003 GWeakRef weak = { { NULL } };
9005 NmcEditorMainCmd cmd;
9007 gboolean cmd_loop = TRUE;
9008 char *cmd_arg = NULL;
9009 char *cmd_arg_s, *cmd_arg_p, *cmd_arg_v;
9010 const char *BASE_PROMPT = "nmcli> ";
9011 const NameItem *valid_settings_main = NULL;
9012 const NameItem *valid_settings_slave = NULL;
9013 char *valid_settings_str = NULL;
9014 const char *s_type = NULL;
9016 AddConnectionInfo *info = NULL;
9018 gboolean temp_changes;
9019 GError *err1 = NULL;
9020 NmcEditorMenuContext menu_ctx;
9022 s_con = nm_connection_get_setting_connection (connection);
9024 s_type = nm_setting_connection_get_slave_type (s_con);
9025 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
9027 valid_settings_main = get_valid_settings_array (connection_type);
9028 valid_settings_slave = get_valid_settings_array (slv_type);
9031 valid_settings_str = get_valid_options_string (valid_settings_main, valid_settings_slave);
9032 g_print (_("You may edit the following settings: %s\n"), valid_settings_str);
9035 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9037 menu_ctx.curr_setting = NULL;
9038 menu_ctx.valid_props = NULL;
9039 menu_ctx.valid_props_str = NULL;
9041 /* Get remote connection */
9042 con_tmp = nm_client_get_connection_by_uuid (nmc->client,
9043 nm_connection_get_uuid (connection));
9044 g_weak_ref_init (&weak, con_tmp);
9045 rem_con = g_weak_ref_get (&weak);
9048 /* Connection is dirty? (not saved or differs from the saved) */
9049 dirty = is_connection_dirty (connection, rem_con);
9050 temp_changes = rem_con ? nm_remote_connection_get_unsaved (rem_con) : TRUE;
9051 if (nmc->editor_status_line)
9052 editor_show_status_line (connection, dirty, temp_changes);
9054 /* Read user input */
9055 cmd_user = nmc_readline ("%s", menu_ctx.main_prompt);
9057 /* Get the remote connection again, it may have disapeared */
9058 removed = refresh_remote_connection (&weak, &rem_con);
9060 g_print (_("The connection profile has been removed from another client. "
9061 "You may type 'save' to restore it.\n"));
9063 if (!cmd_user || *cmd_user == '\0')
9065 cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg);
9070 split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v);
9072 case NMC_EDITOR_MAIN_CMD_SET:
9073 /* Set property value */
9075 if (menu_ctx.level == 1) {
9076 const char *prop_name;
9077 char *prop_val_user = NULL;
9079 GError *tmp_err = NULL;
9081 prop_name = ask_check_property (cmd_arg,
9082 (const char **) menu_ctx.valid_props,
9083 menu_ctx.valid_props_str);
9087 avals = nmc_setting_get_property_allowed_values (menu_ctx.curr_setting, prop_name);
9089 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
9090 g_print (_("Allowed values for '%s' property: %s\n"),
9091 prop_name, avals_str);
9094 prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
9096 /* Set property value */
9097 if (!nmc_setting_set_property (menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) {
9098 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
9099 g_clear_error (&tmp_err);
9102 g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
9103 g_print (_("use 'goto <setting>' first, or 'set <setting>.<property>'\n"));
9106 NMSetting *ss = NULL;
9107 gboolean created_ss = FALSE;
9109 GError *tmp_err = NULL;
9112 /* setting provided as "setting.property" */
9113 ss = is_setting_valid (connection, valid_settings_main, valid_settings_slave, cmd_arg_s);
9115 ss = create_setting_by_name (cmd_arg_s, valid_settings_main, valid_settings_slave);
9117 g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9118 cmd_arg_s, valid_settings_str);
9124 if (menu_ctx.curr_setting)
9125 ss = menu_ctx.curr_setting;
9127 g_print (_("Error: missing setting for '%s' property\n"), cmd_arg_p);
9132 prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9134 g_print (_("Error: invalid property: %s\n"), tmp_err->message);
9135 g_clear_error (&tmp_err);
9137 g_object_unref (ss);
9145 const char **avals = nmc_setting_get_property_allowed_values (ss, prop_name);
9147 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
9148 g_print (_("Allowed values for '%s' property: %s\n"),
9149 prop_name, avals_str);
9152 cmd_arg_v = nmc_readline (_("Enter '%s' value: "), prop_name);
9155 /* Set property value */
9156 if (!nmc_setting_set_property (ss, prop_name, cmd_arg_v, &tmp_err)) {
9157 g_print (_("Error: failed to set '%s' property: %s\n"),
9158 prop_name, tmp_err->message);
9159 g_clear_error (&tmp_err);
9163 nm_connection_add_setting (connection, ss);
9168 case NMC_EDITOR_MAIN_CMD_GOTO:
9169 /* cmd_arg_s != NULL means 'setting.property' argument */
9170 if (menu_ctx.level == 0 || cmd_arg_s) {
9171 /* in top level - no setting selected yet */
9172 const char *setting_name;
9174 const char *user_arg = cmd_arg_s ? cmd_arg_s : cmd_arg_p;
9176 setting_name = ask_check_setting (user_arg,
9177 valid_settings_main,
9178 valid_settings_slave,
9179 valid_settings_str);
9183 setting = nm_connection_get_setting_by_name (connection, setting_name);
9185 setting = nmc_setting_new_for_name (setting_name);
9187 g_print (_("Error: unknown setting '%s'\n"), setting_name);
9190 nmc_setting_custom_init (setting);
9191 nm_connection_add_setting (connection, setting);
9193 /* Set global variable for use in TAB completion */
9194 nmc_tab_completion.setting = setting;
9196 /* Switch to level 1 */
9197 menu_switch_to_level1 (nmc, &menu_ctx, setting, setting_name, nmc->editor_prompt_color);
9200 g_print (_("You may edit the following properties: %s\n"), menu_ctx.valid_props_str);
9204 if (menu_ctx.level == 1 || cmd_arg_s) {
9205 /* level 1 - setting selected */
9206 const char *prop_name;
9208 prop_name = ask_check_property (cmd_arg_p,
9209 (const char **) menu_ctx.valid_props,
9210 menu_ctx.valid_props_str);
9214 /* submenu - level 2 - editing properties */
9215 cmd_loop = property_edit_submenu (nmc,
9219 menu_ctx.curr_setting,
9224 case NMC_EDITOR_MAIN_CMD_REMOVE:
9225 /* Remove setting from connection, or delete value of a property */
9227 if (menu_ctx.level == 1) {
9228 GError *tmp_err = NULL;
9229 const char *prop_name;
9231 prop_name = ask_check_property (cmd_arg,
9232 (const char **) menu_ctx.valid_props,
9233 menu_ctx.valid_props_str);
9237 /* Delete property value */
9238 if (!nmc_setting_reset_property (menu_ctx.curr_setting, prop_name, &tmp_err)) {
9239 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
9241 g_clear_error (&tmp_err);
9244 g_print (_("Error: no argument given; valid are [%s]\n"), valid_settings_str);
9246 NMSetting *ss = NULL;
9250 /* cmd_arg_s != NULL means argument is "setting.property" */
9251 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
9252 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9254 ss = is_setting_valid (connection,
9255 valid_settings_main,
9256 valid_settings_slave,
9259 if (check_valid_name (user_s,
9260 valid_settings_main,
9261 valid_settings_slave,
9263 g_print (_("Setting '%s' is not present in the connection.\n"),
9266 g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9267 user_s, valid_settings_str);
9271 ss = menu_ctx.curr_setting;
9274 /* Remove setting from the connection */
9275 connection_remove_setting (connection, ss);
9276 if (ss == menu_ctx.curr_setting) {
9277 /* If we removed the setting we are in, go up */
9278 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9279 nmc_tab_completion.setting = NULL; /* for TAB completion */
9282 GError *tmp_err = NULL;
9283 char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9285 /* Delete property value */
9286 if (!nmc_setting_reset_property (ss, prop_name, &tmp_err)) {
9287 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
9289 g_clear_error (&tmp_err);
9292 /* If the string is not a property, try it as a setting */
9294 s_tmp = is_setting_valid (connection,
9295 valid_settings_main,
9296 valid_settings_slave,
9299 /* Remove setting from the connection */
9300 connection_remove_setting (connection, s_tmp);
9301 /* coverity[copy_paste_error] - suppress Coverity COPY_PASTE_ERROR defect */
9302 if (ss == menu_ctx.curr_setting) {
9303 /* If we removed the setting we are in, go up */
9304 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9305 nmc_tab_completion.setting = NULL; /* for TAB completion */
9308 g_print (_("Error: %s properties, nor it is a setting name.\n"),
9310 g_clear_error (&tmp_err);
9317 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
9318 /* Print property description */
9320 if (menu_ctx.level == 1) {
9321 const char *prop_name;
9323 prop_name = ask_check_property (cmd_arg,
9324 (const char **) menu_ctx.valid_props,
9325 menu_ctx.valid_props_str);
9329 /* Show property description */
9330 print_property_description (menu_ctx.curr_setting, prop_name);
9332 g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
9333 g_print (_("use 'goto <setting>' first, or 'describe <setting>.<property>'\n"));
9336 NMSetting *ss = NULL;
9337 gboolean unref_ss = FALSE;
9341 /* cmd_arg_s != NULL means argument is "setting.property" */
9342 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
9343 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9345 ss = is_setting_valid (connection,
9346 valid_settings_main,
9347 valid_settings_slave,
9350 ss = create_setting_by_name (user_s,
9351 valid_settings_main,
9352 valid_settings_slave);
9354 g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9355 user_s, valid_settings_str);
9361 ss = menu_ctx.curr_setting;
9364 /* Show description for all properties */
9365 print_setting_description (ss);
9367 GError *tmp_err = NULL;
9368 char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9370 /* Show property description */
9371 print_property_description (ss, prop_name);
9373 /* If the string is not a property, try it as a setting */
9375 s_tmp = is_setting_valid (connection,
9376 valid_settings_main,
9377 valid_settings_slave,
9380 print_setting_description (s_tmp);
9382 g_print (_("Error: invalid property: %s, "
9383 "neither a valid setting name.\n"),
9385 g_clear_error (&tmp_err);
9390 g_object_unref (ss);
9394 case NMC_EDITOR_MAIN_CMD_PRINT:
9395 /* Print current connection settings/properties */
9397 if (strcmp (cmd_arg, "all") == 0)
9398 editor_show_connection (connection, nmc);
9400 NMSetting *ss = NULL;
9401 gboolean whole_setting;
9404 /* cmd_arg_s != NULL means argument is "setting.property" */
9405 whole_setting = !cmd_arg_s && !menu_ctx.curr_setting;
9406 user_s = whole_setting ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9409 s_name = check_valid_name (user_s,
9410 valid_settings_main,
9411 valid_settings_slave,
9414 g_print (_("Error: unknown setting: '%s'\n"), user_s);
9417 ss = nm_connection_get_setting_by_name (connection, s_name);
9419 g_print (_("Error: '%s' setting not present in the connection\n"), s_name);
9423 ss = menu_ctx.curr_setting;
9425 if (whole_setting) {
9426 /* Print the whole setting */
9427 editor_show_setting (ss, nmc);
9430 char *prop_name = is_property_valid (ss, cmd_arg_p, &err);
9432 /* Print one property */
9433 char *prop_val = nmc_setting_get_property (ss, prop_name, NULL);
9434 g_print ("%s.%s: %s\n", nm_setting_get_name (ss),prop_name , prop_val);
9437 /* If the string is not a property, try it as a setting */
9439 s_tmp = is_setting_valid (connection,
9440 valid_settings_main,
9441 valid_settings_slave,
9444 /* Print the whole setting */
9445 editor_show_setting (s_tmp, nmc);
9447 g_print (_("Error: invalid property: %s%s\n"),
9449 cmd_arg_s ? "" : _(", neither a valid setting name"));
9450 g_clear_error (&err);
9456 if (menu_ctx.curr_setting)
9457 editor_show_setting (menu_ctx.curr_setting, nmc);
9459 editor_show_connection (connection, nmc);
9463 case NMC_EDITOR_MAIN_CMD_VERIFY:
9464 /* Verify current setting or the whole connection */
9465 if (cmd_arg && strcmp (cmd_arg, "all") && strcmp (cmd_arg, "fix")) {
9466 g_print (_("Invalid verify option: %s\n"), cmd_arg);
9470 if ( menu_ctx.curr_setting
9471 && (!cmd_arg || strcmp (cmd_arg, "all") != 0)) {
9472 GError *tmp_err = NULL;
9473 (void) nm_setting_verify (menu_ctx.curr_setting, NULL, &tmp_err);
9474 g_print (_("Verify setting '%s': %s\n"),
9475 nm_setting_get_name (menu_ctx.curr_setting),
9476 tmp_err ? tmp_err->message : "OK");
9477 g_clear_error (&tmp_err);
9479 GError *tmp_err = NULL;
9480 gboolean valid, modified;
9481 gboolean fixed = TRUE;
9483 valid = nm_connection_verify (connection, &tmp_err);
9484 if (!valid && (g_strcmp0 (cmd_arg, "fix") == 0)) {
9485 /* Try to fix normalizable errors */
9486 g_clear_error (&tmp_err);
9487 fixed = nm_connection_normalize (connection, NULL, &modified, &tmp_err);
9489 g_print (_("Verify connection: %s\n"),
9490 tmp_err ? tmp_err->message : "OK");
9492 g_print (_("The error cannot be fixed automatically.\n"));
9493 g_clear_error (&tmp_err);
9497 case NMC_EDITOR_MAIN_CMD_SAVE:
9498 /* Save the connection */
9499 if (nm_connection_verify (connection, &err1)) {
9500 gboolean persistent = TRUE;
9502 /* parse argument */
9504 if (matches (cmd_arg, "temporary") == 0)
9506 else if (matches (cmd_arg, "persistent") == 0)
9509 g_print (_("Error: invalid argument '%s'\n"), cmd_arg);
9514 /* Ask for save confirmation if the connection changes to autoconnect=yes */
9515 if (nmc->editor_save_confirmation)
9516 if (!confirm_connection_saving (connection, NM_CONNECTION (rem_con)))
9520 /* Tell the settings service to add the new connection */
9521 info = g_malloc0 (sizeof (AddConnectionInfo));
9523 info->con_name = g_strdup (nm_connection_get_id (connection));
9524 add_new_connection (persistent,
9527 add_connection_editor_cb,
9530 /* Save/update already saved (existing) connection */
9531 nm_connection_replace_settings_from_connection (NM_CONNECTION (rem_con),
9533 update_connection (persistent, rem_con, update_connection_editor_cb, NULL);
9536 g_mutex_lock (&nmc_editor_mutex);
9537 //FIXME: add also a timeout for cases the callback is not called
9538 while (!nmc_editor_cb_called)
9539 g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
9541 if (nmc_editor_error) {
9542 g_print (_("Error: Failed to save '%s' (%s) connection: %s\n"),
9543 nm_connection_get_id (connection),
9544 nm_connection_get_uuid (connection),
9545 nmc_editor_error->message);
9546 g_error_free (nmc_editor_error);
9549 _("Connection '%s' (%s) successfully saved.\n") :
9550 _("Connection '%s' (%s) successfully updated.\n"),
9551 nm_connection_get_id (connection),
9552 nm_connection_get_uuid (connection));
9554 con_tmp = nm_client_get_connection_by_uuid (nmc->client,
9555 nm_connection_get_uuid (connection));
9556 g_weak_ref_set (&weak, con_tmp);
9557 refresh_remote_connection (&weak, &rem_con);
9559 /* Replace local connection with the remote one to be sure they are equal.
9560 * This mitigates problems with plugins not preserving some properties or
9561 * adding ipv{4,6} settings when not present.
9564 char *s_name = NULL;
9565 if (menu_ctx.curr_setting)
9566 s_name = g_strdup (nm_setting_get_name (menu_ctx.curr_setting));
9568 /* Update settings in the local connection */
9569 nm_connection_replace_settings_from_connection (connection,
9570 NM_CONNECTION (con_tmp));
9572 /* Also update setting for menu context and TAB-completion */
9573 menu_ctx.curr_setting = s_name ? nm_connection_get_setting_by_name (connection, s_name) : NULL;
9574 nmc_tab_completion.setting = menu_ctx.curr_setting;
9579 nmc_editor_cb_called = FALSE;
9580 nmc_editor_error = NULL;
9581 g_mutex_unlock (&nmc_editor_mutex);
9583 g_print (_("Error: connection verification failed: %s\n"),
9584 err1 ? err1->message : _("(unknown error)"));
9585 g_print (_("You may try running 'verify fix' to fix errors.\n"));
9588 g_clear_error (&err1);
9591 case NMC_EDITOR_MAIN_CMD_ACTIVATE:
9593 GError *tmp_err = NULL;
9594 const char *ifname = cmd_arg_p;
9595 const char *ap_nsp = cmd_arg_v;
9597 /* When only AP/NSP is specified it is prepended with '/' */
9599 if (ifname && ifname[0] == '/') {
9600 ap_nsp = ifname + 1;
9604 ap_nsp = ap_nsp && ap_nsp[0] == '/' ? ap_nsp + 1 : ap_nsp;
9606 if (is_connection_dirty (connection, rem_con)) {
9607 g_print (_("Error: connection is not saved. Type 'save' first.\n"));
9610 if (!nm_connection_verify (NM_CONNECTION (rem_con), &tmp_err)) {
9611 g_print (_("Error: connection is not valid: %s\n"), tmp_err->message);
9612 g_clear_error (&tmp_err);
9616 nmc->nowait_flag = FALSE;
9618 nmc->print_output = NMC_PRINT_PRETTY;
9619 if (!nmc_activate_connection (nmc, NM_CONNECTION (rem_con), ifname, ap_nsp, ap_nsp, NULL,
9620 activate_connection_editor_cb, &tmp_err)) {
9621 g_print (_("Error: Cannot activate connection: %s.\n"), tmp_err->message);
9622 g_clear_error (&tmp_err);
9626 g_mutex_lock (&nmc_editor_mutex);
9627 while (!nmc_editor_cb_called)
9628 g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
9630 if (nmc_editor_error) {
9631 g_print (_("Error: Failed to activate '%s' (%s) connection: %s\n"),
9632 nm_connection_get_id (connection),
9633 nm_connection_get_uuid (connection),
9634 nmc_editor_error->message);
9635 g_error_free (nmc_editor_error);
9637 g_print (_("Monitoring connection activation (press any key to continue)\n"));
9638 nmc_get_user_input ("");
9641 if (nmc_editor_monitor_ac) {
9642 if (nmc_editor_monitor_ac->monitor_id)
9643 g_source_remove (nmc_editor_monitor_ac->monitor_id);
9644 g_free (nmc_editor_monitor_ac);
9646 nmc_editor_cb_called = FALSE;
9647 nmc_editor_error = NULL;
9648 nmc_editor_monitor_ac = NULL;
9649 g_mutex_unlock (&nmc_editor_mutex);
9651 /* Update timestamp in local connection */
9652 update_connection_timestamp (NM_CONNECTION (rem_con), connection);
9657 case NMC_EDITOR_MAIN_CMD_BACK:
9658 /* Go back (up) an the menu */
9659 if (menu_ctx.level == 1) {
9660 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9661 nmc_tab_completion.setting = NULL; /* for TAB completion */
9665 case NMC_EDITOR_MAIN_CMD_HELP:
9666 /* Print command help */
9667 editor_main_help (cmd_arg);
9670 case NMC_EDITOR_MAIN_CMD_NMCLI:
9671 if (cmd_arg_p && matches (cmd_arg_p, "status-line") == 0) {
9672 GError *tmp_err = NULL;
9674 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9675 g_print (_("Error: status-line: %s\n"), tmp_err->message);
9676 g_clear_error (&tmp_err);
9678 nmc->editor_status_line = bb;
9679 } else if (cmd_arg_p && matches (cmd_arg_p, "save-confirmation") == 0) {
9680 GError *tmp_err = NULL;
9682 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9683 g_print (_("Error: save-confirmation: %s\n"), tmp_err->message);
9684 g_clear_error (&tmp_err);
9686 nmc->editor_save_confirmation = bb;
9687 } else if (cmd_arg_p && matches (cmd_arg_p, "show-secrets") == 0) {
9688 GError *tmp_err = NULL;
9690 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9691 g_print (_("Error: show-secrets: %s\n"), tmp_err->message);
9692 g_clear_error (&tmp_err);
9694 nmc->editor_show_secrets = bb;
9695 } else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color") == 0) {
9696 GError *tmp_err = NULL;
9698 color = nmc_term_color_parse_string (cmd_arg_v ? g_strstrip (cmd_arg_v) : " ", &tmp_err);
9700 g_print (_("Error: bad color: %s\n"), tmp_err->message);
9701 g_clear_error (&tmp_err);
9703 nmc->editor_prompt_color = color;
9704 g_free (menu_ctx.main_prompt);
9705 if (menu_ctx.level == 0)
9706 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9709 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9711 nm_setting_get_name (menu_ctx.curr_setting));
9713 } else if (!cmd_arg_p) {
9714 g_print (_("Current nmcli configuration:\n"));
9715 g_print ("status-line: %s\n"
9716 "save-confirmation: %s\n"
9717 "show-secrets: %s\n"
9718 "prompt-color: %d\n",
9719 nmc->editor_status_line ? "yes" : "no",
9720 nmc->editor_save_confirmation ? "yes" : "no",
9721 nmc->editor_show_secrets ? "yes" : "no",
9722 nmc->editor_prompt_color);
9724 g_print (_("Invalid configuration option '%s'; allowed [%s]\n"),
9725 cmd_arg_v ? cmd_arg_v : "", "status-line, save-confirmation, show-secrets, prompt-color");
9729 case NMC_EDITOR_MAIN_CMD_QUIT:
9730 if (is_connection_dirty (connection, rem_con)) {
9731 if (confirm_quit ())
9732 cmd_loop = FALSE; /* quit command loop */
9734 cmd_loop = FALSE; /* quit command loop */
9737 case NMC_EDITOR_MAIN_CMD_UNKNOWN:
9739 g_print (_("Unknown command: '%s'\n"), cmd_user);
9749 g_free (valid_settings_str);
9750 g_free (menu_ctx.main_prompt);
9751 g_strfreev (menu_ctx.valid_props);
9752 g_free (menu_ctx.valid_props_str);
9754 g_object_unref (rem_con);
9755 g_weak_ref_clear (&weak);
9757 /* Save history file */
9758 save_history_cmds (nm_connection_get_uuid (connection));
9764 get_ethernet_device_name (NmCli *nmc)
9766 const GPtrArray *devices;
9769 devices = nm_client_get_devices (nmc->client);
9770 for (i = 0; i < devices->len; i++) {
9771 NMDevice *dev = g_ptr_array_index (devices, i);
9772 if (NM_IS_DEVICE_ETHERNET (dev))
9773 return nm_device_get_iface (dev);
9779 editor_init_new_connection (NmCli *nmc, NMConnection *connection)
9781 NMSetting *setting, *base_setting;
9782 NMSettingConnection *s_con;
9783 const char *con_type;
9784 const char *slave_type = NULL;
9786 s_con = nm_connection_get_setting_connection (connection);
9788 con_type = nm_setting_connection_get_connection_type (s_con);
9790 /* Initialize new connection according to its type using sensible defaults. */
9792 nmc_setting_connection_connect_handlers (s_con, connection);
9794 if (g_strcmp0 (con_type, "bond-slave") == 0)
9795 slave_type = NM_SETTING_BOND_SETTING_NAME;
9796 if (g_strcmp0 (con_type, "team-slave") == 0)
9797 slave_type = NM_SETTING_TEAM_SETTING_NAME;
9798 if (g_strcmp0 (con_type, "bridge-slave") == 0)
9799 slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
9802 const char *dev_ifname = get_ethernet_device_name (nmc);
9804 /* For bond/team/bridge slaves add 'wired' setting */
9805 setting = nm_setting_wired_new ();
9806 nm_connection_add_setting (connection, setting);
9808 g_object_set (s_con,
9809 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
9810 NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
9811 NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
9814 /* Add a "base" setting to the connection by default */
9815 base_setting = nmc_setting_new_for_name (con_type);
9818 nm_connection_add_setting (connection, base_setting);
9820 /* Set a sensible bond/team/bridge interface name by default */
9821 if (g_strcmp0 (con_type, NM_SETTING_BOND_SETTING_NAME) == 0)
9822 g_object_set (s_con,
9823 NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bond",
9825 if (g_strcmp0 (con_type, NM_SETTING_TEAM_SETTING_NAME) == 0)
9826 g_object_set (s_con,
9827 NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-team",
9829 if (g_strcmp0 (con_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0)
9830 g_object_set (s_con,
9831 NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bridge",
9834 /* Set sensible initial VLAN values */
9835 if (g_strcmp0 (con_type, NM_SETTING_VLAN_SETTING_NAME) == 0) {
9836 const char *dev_ifname = get_ethernet_device_name (nmc);
9838 g_object_set (NM_SETTING_VLAN (base_setting),
9839 NM_SETTING_VLAN_PARENT, dev_ifname ? dev_ifname : "eth0",
9840 NM_SETTING_VLAN_ID, 1,
9842 g_object_set (s_con,
9843 NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
9844 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_VLAN_SETTING_NAME,
9848 /* Initialize 'transport-mode' so that 'infiniband' is valid */
9849 if (g_strcmp0 (con_type, NM_SETTING_INFINIBAND_SETTING_NAME) == 0)
9850 g_object_set (NM_SETTING_INFINIBAND (base_setting),
9851 NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram",
9854 /* Initialize 'number' so that 'cdma' is valid */
9855 if (g_strcmp0 (con_type, NM_SETTING_CDMA_SETTING_NAME) == 0)
9856 g_object_set (NM_SETTING_CDMA (base_setting),
9857 NM_SETTING_CDMA_NUMBER, "#777",
9860 /* Initialize 'number' so that 'gsm' is valid */
9861 if (g_strcmp0 (con_type, NM_SETTING_GSM_SETTING_NAME) == 0)
9862 g_object_set (NM_SETTING_GSM (base_setting),
9863 NM_SETTING_GSM_NUMBER, "*99#",
9867 if (g_strcmp0 (con_type, NM_SETTING_WIRELESS_SETTING_NAME) == 0) {
9868 /* For Wi-Fi set mode to "infrastructure". Even though mode == NULL
9869 * is regarded as "infrastructure", explicit value makes no doubts.
9871 g_object_set (NM_SETTING_WIRELESS (base_setting),
9872 NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
9875 /* Do custom initialization for wifi setting */
9876 nmc_setting_custom_init (base_setting);
9880 if (g_strcmp0 (con_type, NM_SETTING_ADSL_SETTING_NAME) == 0) {
9881 /* Initialize a protocol */
9882 g_object_set (NM_SETTING_ADSL (base_setting),
9883 NM_SETTING_ADSL_PROTOCOL, NM_SETTING_ADSL_PROTOCOL_PPPOE,
9887 /* Always add IPv4 and IPv6 settings for non-slave connections */
9888 setting = nm_setting_ip4_config_new ();
9889 nmc_setting_custom_init (setting);
9890 nm_connection_add_setting (connection, setting);
9892 setting = nm_setting_ip6_config_new ();
9893 nmc_setting_custom_init (setting);
9894 nm_connection_add_setting (connection, setting);
9899 editor_init_existing_connection (NMConnection *connection)
9901 NMSettingIPConfig *s_ip4, *s_ip6;
9902 NMSettingWireless *s_wireless;
9903 NMSettingConnection *s_con;
9905 s_ip4 = nm_connection_get_setting_ip4_config (connection);
9906 s_ip6 = nm_connection_get_setting_ip6_config (connection);
9907 s_wireless = nm_connection_get_setting_wireless (connection);
9908 s_con = nm_connection_get_setting_connection (connection);
9911 nmc_setting_ip4_connect_handlers (s_ip4);
9913 nmc_setting_ip6_connect_handlers (s_ip6);
9915 nmc_setting_wireless_connect_handlers (s_wireless);
9917 nmc_setting_connection_connect_handlers (s_con, connection);
9920 static NMCResultCode
9921 do_connection_edit (NmCli *nmc, int argc, char **argv)
9923 NMConnection *connection = NULL;
9924 NMSettingConnection *s_con;
9925 const char *connection_type;
9927 char *default_name = NULL;
9928 const char *type = NULL;
9929 char *type_ask = NULL;
9930 const char *con_name = NULL;
9931 const char *con = NULL;
9932 const char *con_id = NULL;
9933 const char *con_uuid = NULL;
9934 const char *con_path = NULL;
9935 const char *selector = NULL;
9937 GError *error = NULL;
9938 GError *err1 = NULL;
9939 nmc_arg_t exp_args[] = { {"type", TRUE, &type, FALSE},
9940 {"con-name", TRUE, &con_name, FALSE},
9941 {"id", TRUE, &con_id, FALSE},
9942 {"uuid", TRUE, &con_uuid, FALSE},
9943 {"path", TRUE, &con_path, FALSE},
9946 nmc->return_value = NMC_RESULT_SUCCESS;
9951 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, &error)) {
9952 g_string_assign (nmc->return_text, error->message);
9953 nmc->return_value = error->code;
9954 g_clear_error (&error);
9959 /* Setup some readline completion stuff */
9960 /* Set a pointer to an alternative function to create matches */
9961 rl_attempted_completion_function = (rl_completion_func_t *) nmcli_editor_tab_completion;
9962 /* Use ' ' and '.' as word break characters */
9963 rl_completer_word_break_characters = ". ";
9966 if (con_id && !con_uuid && !con_path) {
9969 } else if (con_uuid && !con_id && !con_path) {
9972 } else if (con_path && !con_id && !con_uuid) {
9975 } else if (!con_path && !con_id && !con_uuid) {
9978 g_string_printf (nmc->return_text,
9979 _("Error: only one of 'id', uuid, or 'path' can be provided."));
9980 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
9986 /* Existing connection */
9987 NMConnection *found_con;
9989 found_con = nmc_find_connection (nmc->connections, selector, con, NULL);
9991 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con);
9992 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
9996 /* Duplicate the connection and use that so that we need not
9997 * differentiate existing vs. new later
9999 connection = nm_simple_connection_new_clone (found_con);
10001 /* Merge secrets into the connection */
10002 update_secrets_in_connection (NM_REMOTE_CONNECTION (found_con), connection);
10004 s_con = nm_connection_get_setting_connection (connection);
10006 connection_type = nm_setting_connection_get_connection_type (s_con);
10009 g_print (_("Warning: editing existing connection '%s'; 'type' argument is ignored\n"),
10010 nm_connection_get_id (connection));
10012 g_print (_("Warning: editing existing connection '%s'; 'con-name' argument is ignored\n"),
10013 nm_connection_get_id (connection));
10015 /* Load previously saved history commands for the connection */
10016 load_history_cmds (nm_connection_get_uuid (connection));
10018 editor_init_existing_connection (connection);
10020 /* New connection */
10021 connection_type = check_valid_name (type, nmc_valid_connection_types, NULL, &err1);
10022 tmp_str = get_valid_options_string (nmc_valid_connection_types, NULL);
10024 while (!connection_type) {
10026 g_print (_("Valid connection types: %s\n"), tmp_str);
10028 g_print (_("Error: invalid connection type; %s\n"), err1->message);
10029 g_clear_error (&err1);
10031 type_ask = nmc_readline (EDITOR_PROMPT_CON_TYPE);
10032 type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
10033 connection_type = check_valid_name (type_ask, nmc_valid_connection_types, NULL, &err1);
10038 /* Create a new connection object */
10039 connection = nm_simple_connection_new ();
10041 /* Build up the 'connection' setting */
10042 s_con = (NMSettingConnection *) nm_setting_connection_new ();
10043 uuid = nm_utils_uuid_generate ();
10045 default_name = g_strdup (con_name);
10047 default_name = nmc_unique_connection_name (nmc->connections,
10048 get_name_alias (connection_type, nmc_valid_connection_types));
10050 g_object_set (s_con,
10051 NM_SETTING_CONNECTION_ID, default_name,
10052 NM_SETTING_CONNECTION_UUID, uuid,
10053 NM_SETTING_CONNECTION_TYPE, connection_type,
10056 g_free (default_name);
10057 nm_connection_add_setting (connection, NM_SETTING (s_con));
10059 /* Initialize the new connection so that it is valid from the start */
10060 editor_init_new_connection (nmc, connection);
10063 /* nmcli runs the editor */
10064 nmc->in_editor = TRUE;
10067 g_print (_("===| nmcli interactive connection editor |==="));
10070 g_print (_("Editing existing '%s' connection: '%s'"), connection_type, con);
10072 g_print (_("Adding a new '%s' connection"), connection_type);
10074 g_print (_("Type 'help' or '?' for available commands."));
10076 g_print (_("Type 'describe [<setting>.<prop>]' for detailed property description."));
10079 /* Set global variables for use in TAB completion */
10080 nmc_tab_completion.nmc = nmc;
10081 nmc_tab_completion.con_type = g_strdup (connection_type);
10082 nmc_tab_completion.connection = connection;
10084 /* Run menu loop */
10085 editor_menu_main (nmc, connection, connection_type);
10088 g_object_unref (connection);
10089 g_free (nmc_tab_completion.con_type);
10091 nmc->should_wait++;
10092 return nmc->return_value;
10095 g_assert (!connection);
10098 nmc->should_wait++;
10099 return nmc->return_value;
10104 modify_connection_cb (GObject *connection,
10105 GAsyncResult *result,
10106 gpointer user_data)
10108 NmCli *nmc = (NmCli *) user_data;
10109 GError *error = NULL;
10111 if (!nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
10113 g_string_printf (nmc->return_text,
10114 _("Error: Failed to modify connection '%s': %s"),
10115 nm_connection_get_id (NM_CONNECTION (connection)),
10117 g_error_free (error);
10118 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10120 if (nmc->print_output == NMC_PRINT_PRETTY)
10121 g_print (_("Connection '%s' (%s) successfully modified.\n"),
10122 nm_connection_get_id (NM_CONNECTION (connection)),
10123 nm_connection_get_uuid (NM_CONNECTION (connection)));
10128 static NMCResultCode
10129 do_connection_modify (NmCli *nmc,
10130 gboolean temporary,
10134 NMConnection *connection = NULL;
10135 NMRemoteConnection *rc = NULL;
10137 const char *selector = NULL;
10138 GError *error = NULL;
10140 nmc->return_value = NMC_RESULT_SUCCESS;
10143 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10144 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10147 if ( strcmp (*argv, "id") == 0
10148 || strcmp (*argv, "uuid") == 0
10149 || strcmp (*argv, "path") == 0) {
10152 if (next_arg (&argc, &argv) != 0) {
10153 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10155 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10162 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10163 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10166 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10168 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10169 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10172 rc = nm_client_get_connection_by_uuid (nmc->client,
10173 nm_connection_get_uuid (connection));
10175 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10176 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10180 if (next_arg (&argc, &argv) != 0) {
10181 g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing."));
10182 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10186 if (!read_connection_properties (NM_CONNECTION (rc), argc, argv, &error)) {
10187 g_string_assign (nmc->return_text, error->message);
10188 nmc->return_value = error->code;
10189 g_clear_error (&error);
10193 update_connection (!temporary, rc, modify_connection_cb, nmc);
10195 nmc->should_wait++;
10197 return nmc->return_value;
10205 } CloneConnectionInfo;
10208 clone_connection_cb (GObject *client,
10209 GAsyncResult *result,
10210 gpointer user_data)
10212 CloneConnectionInfo *info = (CloneConnectionInfo *) user_data;
10213 NmCli *nmc = info->nmc;
10214 NMRemoteConnection *connection;
10215 GError *error = NULL;
10217 connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
10219 g_string_printf (nmc->return_text,
10220 _("Error: Failed to add '%s' connection: %s"),
10221 info->con_id, error->message);
10222 g_error_free (error);
10223 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
10225 g_print (_("%s (%s) cloned as %s (%s).\n"),
10228 nm_connection_get_id (NM_CONNECTION (connection)),
10229 nm_connection_get_uuid (NM_CONNECTION (connection)));
10230 g_object_unref (connection);
10233 g_free (info->con_id);
10234 g_free (info->orig_id);
10235 g_free (info->orig_uuid);
10236 g_slice_free (CloneConnectionInfo, info);
10240 static NMCResultCode
10241 do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv)
10243 NMConnection *connection = NULL;
10244 NMConnection *new_connection = NULL;
10245 NMSettingConnection *s_con;
10246 CloneConnectionInfo *info;
10248 const char *new_name;
10249 char *name_ask = NULL;
10250 char *new_name_ask = NULL;
10251 const char *selector = NULL;
10256 name = name_ask = nmc_readline (PROMPT_CONNECTION);
10257 new_name = new_name_ask = nmc_readline (_("New connection name: "));
10259 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10260 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10264 if ( strcmp (*argv, "id") == 0
10265 || strcmp (*argv, "uuid") == 0
10266 || strcmp (*argv, "path") == 0) {
10269 if (next_arg (&argc, &argv) != 0) {
10270 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10272 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10277 if (next_arg (&argc, &argv) != 0) {
10278 g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
10279 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10283 if (next_arg (&argc, &argv) == 0) {
10284 g_string_printf (nmc->return_text, _("Error: unexpected extra argument '%s'."), *argv);
10285 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10291 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10292 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10296 g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
10297 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10301 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10303 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10304 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10308 /* Copy the connection */
10309 new_connection = nm_simple_connection_new_clone (connection);
10311 s_con = nm_connection_get_setting_connection (new_connection);
10313 uuid = nm_utils_uuid_generate ();
10314 g_object_set (s_con,
10315 NM_SETTING_CONNECTION_ID, new_name,
10316 NM_SETTING_CONNECTION_UUID, uuid,
10320 /* Merge secrets into the new connection */
10321 update_secrets_in_connection (NM_REMOTE_CONNECTION (connection), new_connection);
10323 info = g_slice_new0 (CloneConnectionInfo);
10325 info->orig_id = g_strdup (nm_connection_get_id (connection));
10326 info->orig_uuid = g_strdup (nm_connection_get_uuid (connection));
10327 info->con_id = g_strdup (nm_connection_get_id (new_connection));
10329 /* Add the new cloned connection to NetworkManager */
10330 add_new_connection (!temporary,
10333 clone_connection_cb,
10336 nmc->should_wait = TRUE;
10338 if (new_connection)
10339 g_object_unref (new_connection);
10341 g_free (new_name_ask);
10343 return nmc->return_value;
10347 delete_cb (GObject *con, GAsyncResult *result, gpointer user_data)
10349 ConnectionCbInfo *info = (ConnectionCbInfo *) user_data;
10350 GError *error = NULL;
10352 if (!nm_remote_connection_delete_finish (NM_REMOTE_CONNECTION (con), result, &error)) {
10353 g_string_printf (info->nmc->return_text, _("Error: not all connections deleted."));
10354 g_printerr (_("Error: Connection deletion failed: %s"),
10356 g_error_free (error);
10357 info->nmc->return_value = NMC_RESULT_ERROR_CON_DEL;
10358 connection_cb_info_finish (info, con);
10360 if (info->nmc->nowait_flag)
10361 connection_cb_info_finish (info, con);
10365 static NMCResultCode
10366 do_connection_delete (NmCli *nmc, int argc, char **argv)
10368 NMConnection *connection;
10369 ConnectionCbInfo *info = NULL;
10370 GSList *queue = NULL, *iter;
10371 char **arg_arr = NULL;
10372 char **arg_ptr = argv;
10373 int arg_num = argc;
10374 GString *invalid_cons = NULL;
10377 if (nmc->timeout == -1)
10382 char *line = nmc_readline (PROMPT_CONNECTIONS);
10383 nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
10387 if (arg_num == 0) {
10388 g_string_printf (nmc->return_text, _("Error: No connection specified."));
10389 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10394 while (arg_num > 0) {
10395 const char *selector = NULL;
10397 if ( strcmp (*arg_ptr, "id") == 0
10398 || strcmp (*arg_ptr, "uuid") == 0
10399 || strcmp (*arg_ptr, "path") == 0) {
10400 selector = *arg_ptr;
10401 if (next_arg (&arg_num, &arg_ptr) != 0) {
10402 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
10403 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10408 connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos);
10410 /* Check if the connection is unique. */
10411 /* Calling delete for the same connection repeatedly would result in
10412 * NM responding for the last D-Bus call only and we would stall. */
10413 if (!g_slist_find (queue, connection))
10414 queue = g_slist_prepend (queue, g_object_ref (connection));
10416 g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr);
10417 g_string_printf (nmc->return_text, _("Error: not all active connections found."));
10418 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10420 invalid_cons = g_string_new (NULL);
10421 g_string_append_printf (invalid_cons, "'%s', ", *arg_ptr);
10424 /* Take next argument (if there's no other connection of the same name) */
10426 next_arg (&arg_num, &arg_ptr);
10430 g_string_printf (nmc->return_text, _("Error: no connection provided."));
10431 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10434 queue = g_slist_reverse (queue);
10436 info = g_slice_new0 (ConnectionCbInfo);
10438 info->queue = queue;
10439 info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
10441 nmc->nowait_flag = (nmc->timeout == 0);
10442 nmc->should_wait++;
10444 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED,
10445 G_CALLBACK (connection_removed_cb), info);
10447 /* Now delete the connections */
10448 for (iter = queue; iter; iter = g_slist_next (iter))
10449 nm_remote_connection_delete_async (NM_REMOTE_CONNECTION (iter->data),
10450 NULL, delete_cb, info);
10453 if (invalid_cons) {
10454 g_string_truncate (invalid_cons, invalid_cons->len-2); /* truncate trailing ", " */
10455 g_string_printf (nmc->return_text, _("Error: cannot delete unknown connection(s): %s."),
10456 invalid_cons->str);
10457 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10458 g_string_free (invalid_cons, TRUE);
10460 g_strfreev (arg_arr);
10461 return nmc->return_value;
10465 connection_changed (NMConnection *connection, NmCli *nmc)
10467 g_print (_("%s: connection profile changed\n"), nm_connection_get_id (connection));
10471 connection_watch (NmCli *nmc, NMConnection *connection)
10473 nmc->should_wait++;
10474 g_signal_connect (connection, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed), nmc);
10478 connection_unwatch (NmCli *nmc, NMConnection *connection)
10480 if (g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_changed), nmc))
10481 nmc->should_wait--;
10483 /* Terminate if all the watched connections disappeared. */
10484 if (!nmc->should_wait)
10489 connection_added (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
10491 NMConnection *connection = NM_CONNECTION (con);
10493 g_print (_("%s: connection profile created\n"), nm_connection_get_id (connection));
10494 connection_watch (nmc, connection);
10498 connection_removed (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
10500 NMConnection *connection = NM_CONNECTION (con);
10502 g_print (_("%s: connection profile removed\n"), nm_connection_get_id (connection));
10503 connection_unwatch (nmc, connection);
10506 static NMCResultCode
10507 do_connection_monitor (NmCli *nmc, int argc, char **argv)
10510 /* No connections specified. Monitor all. */
10513 nmc->connections = nm_client_get_connections (nmc->client);
10514 for (i = 0; i < nmc->connections->len; i++)
10515 connection_watch (nmc, g_ptr_array_index (nmc->connections, i));
10517 /* We'll watch the connection additions too, never exit. */
10518 nmc->should_wait++;
10519 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_ADDED, G_CALLBACK (connection_added), nmc);
10521 /* Look up the specified connections and watch them. */
10522 NMConnection *connection;
10523 char **arg_ptr = argv;
10524 int arg_num = argc;
10528 const char *selector = NULL;
10530 if ( strcmp (*arg_ptr, "id") == 0
10531 || strcmp (*arg_ptr, "uuid") == 0
10532 || strcmp (*arg_ptr, "path") == 0) {
10533 selector = *arg_ptr;
10534 if (next_arg (&arg_num, &arg_ptr) != 0) {
10535 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
10536 return NMC_RESULT_ERROR_USER_INPUT;
10540 connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos);
10542 connection_watch (nmc, connection);
10544 g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr);
10545 g_string_printf (nmc->return_text, _("Error: not all connections found."));
10546 return NMC_RESULT_ERROR_NOT_FOUND;
10549 /* Take next argument (if there's no other connection of the same name) */
10551 next_arg (&arg_num, &arg_ptr);
10552 } while (arg_num > 0);
10555 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED, G_CALLBACK (connection_removed), nmc);
10557 return NMC_RESULT_SUCCESS;
10560 static NMCResultCode
10561 do_connection_reload (NmCli *nmc, int argc, char **argv)
10563 GError *error = NULL;
10565 nmc->return_value = NMC_RESULT_SUCCESS;
10567 if (!nm_client_reload_connections (nmc->client, NULL, &error)) {
10568 g_string_printf (nmc->return_text, _("Error: failed to reload connections: %s."),
10570 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10571 g_clear_error (&error);
10574 return nmc->return_value;
10577 static NMCResultCode
10578 do_connection_load (NmCli *nmc, int argc, char **argv)
10580 GError *error = NULL;
10581 char **filenames, **failures = NULL;
10584 nmc->return_value = NMC_RESULT_SUCCESS;
10587 g_string_printf (nmc->return_text, _("Error: No connection specified."));
10588 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10589 return nmc->return_value;
10592 filenames = g_new (char *, argc + 1);
10593 for (i = 0; i < argc; i++)
10594 filenames[i] = argv[i];
10595 filenames[i] = NULL;
10597 nm_client_load_connections (nmc->client, filenames, &failures, NULL, &error);
10598 g_free (filenames);
10600 g_string_printf (nmc->return_text, _("Error: failed to load connection: %s."),
10602 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10603 g_error_free (error);
10607 for (i = 0; failures[i]; i++)
10608 g_printerr (_("Could not load file '%s'\n"), failures[i]);
10609 g_strfreev (failures);
10612 return nmc->return_value;
10615 // FIXME: change the text when non-VPN connection types are supported
10616 #define PROMPT_IMPORT_TYPE PROMPT_VPN_TYPE
10617 #define PROMPT_IMPORT_FILE _("File to import: ")
10619 static NMCResultCode
10620 do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv)
10622 GError *error = NULL;
10623 const char *type = NULL, *filename = NULL;
10624 char *type_ask = NULL, *filename_ask = NULL;
10625 AddConnectionInfo *info;
10626 NMConnection *connection = NULL;
10627 NMVpnEditorPlugin *plugin;
10631 type_ask = nmc_readline (PROMPT_IMPORT_TYPE);
10632 filename_ask = nmc_readline (PROMPT_IMPORT_FILE);
10633 type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
10634 filename = filename_ask = filename_ask ? g_strstrip (filename_ask) : NULL;
10636 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10637 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10643 if (strcmp (*argv, "type") == 0) {
10644 if (next_arg (&argc, &argv) != 0) {
10645 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
10646 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10652 g_printerr (_("Warning: 'type' already specified, ignoring extra one.\n"));
10654 } else if (strcmp (*argv, "file") == 0) {
10655 if (next_arg (&argc, &argv) != 0) {
10656 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
10657 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10663 g_printerr (_("Warning: 'file' already specified, ignoring extra one.\n"));
10665 g_string_printf (nmc->return_text, _("Unknown parameter: %s"), *argv);
10666 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10675 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
10676 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10680 g_string_printf (nmc->return_text, _("Error: 'file' argument is required."));
10681 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10685 /* Import VPN configuration */
10686 plugin = nm_vpn_get_plugin_by_service (type, &error);
10688 g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
10690 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10694 connection = nm_vpn_editor_plugin_import (plugin, filename, &error);
10696 g_string_printf (nmc->return_text, _("Error: failed to import '%s': %s."),
10697 filename, error->message);
10698 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10702 info = g_malloc0 (sizeof (AddConnectionInfo));
10704 info->con_name = g_strdup (nm_connection_get_id (connection));
10706 /* Add the new imported connection to NetworkManager */
10707 add_new_connection (!temporary,
10713 nmc->should_wait = TRUE;
10716 g_object_unref (connection);
10717 g_clear_error (&error);
10719 g_free (filename_ask);
10720 return nmc->return_value;
10723 static NMCResultCode
10724 do_connection_export (NmCli *nmc, int argc, char **argv)
10726 NMConnection *connection = NULL;
10728 const char *out_name = NULL;
10729 char *name_ask = NULL;
10730 char *out_name_ask = NULL;
10731 const char *path = NULL;
10732 const char *selector = NULL;
10733 const char *type = NULL;
10734 NMVpnEditorPlugin *plugin;
10735 GError *error = NULL;
10736 char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX";
10740 name_ask = nmc_readline (PROMPT_VPN_CONNECTION);
10741 name = name_ask = name_ask ? g_strstrip (name_ask) : NULL;
10742 out_name = out_name_ask = nmc_readline (_("Output file name: "));
10744 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10745 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10749 if ( strcmp (*argv, "id") == 0
10750 || strcmp (*argv, "uuid") == 0
10751 || strcmp (*argv, "path") == 0) {
10754 if (next_arg (&argc, &argv) != 0) {
10755 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10757 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10762 if (next_arg (&argc, &argv) == 0)
10765 if (next_arg (&argc, &argv) == 0) {
10766 g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv);
10767 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10773 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10774 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10777 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10779 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10780 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10784 type = nm_connection_get_connection_type (connection);
10785 if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) != 0) {
10786 g_string_printf (nmc->return_text, _("Error: the connection is not VPN."));
10787 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10790 type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
10792 /* Export VPN configuration */
10793 plugin = nm_vpn_get_plugin_by_service (type, &error);
10795 g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
10797 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10805 fd = g_mkstemp (tmpfile);
10807 g_string_printf (nmc->return_text, _("Error: failed to create temporary file %s."), tmpfile);
10808 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10815 if (!nm_vpn_editor_plugin_export (plugin, path, connection, &error)) {
10816 g_string_printf (nmc->return_text, _("Error: failed to export '%s': %s."),
10817 nm_connection_get_id (connection), error->message);
10818 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10822 /* No output file -> copy data to stdout */
10824 char *contents = NULL;
10826 if (!g_file_get_contents (path, &contents, &len, &error)) {
10827 g_string_printf (nmc->return_text, _("Error: failed to read temporary file '%s': %s."),
10828 path, error->message);
10829 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10832 g_print ("%s", contents);
10837 if (!out_name && path)
10839 g_clear_error (&error);
10841 g_free (out_name_ask);
10842 return nmc->return_value;
10850 } NmcEditorThreadData;
10852 static GThread *editor_thread;
10853 static NmcEditorThreadData editor_thread_data;
10856 * We need to run do_connection_edit() in a thread so that
10857 * glib main loop is not blocked and could receive and process D-Bus
10861 connection_editor_thread_func (gpointer data)
10863 NmcEditorThreadData *td = (NmcEditorThreadData *) data;
10865 /* run editor for editing/adding connections */
10866 td->nmc->return_value = do_connection_edit (td->nmc, td->argc, td->argv);
10868 /* quit glib main loop now that we are done with this thread */
10875 gen_func_connection_names (const char *text, int state)
10878 const char **connections;
10881 if (nm_cli.connections->len == 0)
10884 connections = g_new (const char *, nm_cli.connections->len + 1);
10885 for (i = 0; i < nm_cli.connections->len; i++) {
10886 NMConnection *con = NM_CONNECTION (nm_cli.connections->pdata[i]);
10887 const char *id = nm_connection_get_id (con);
10888 connections[i] = id;
10890 connections[i] = NULL;
10892 ret = nmc_rl_gen_func_basic (text, state, connections);
10894 g_free (connections);
10899 gen_func_active_connection_names (const char *text, int state)
10902 const GPtrArray *acs;
10903 const char **connections;
10906 if (!nm_cli.client)
10909 acs = nm_client_get_active_connections (nm_cli.client);
10910 if (!acs || acs->len == 0)
10913 connections = g_new (const char *, acs->len + 1);
10914 for (i = 0; i < acs->len; i++)
10915 connections[i] = nm_active_connection_get_id (acs->pdata[i]);
10916 connections[i] = NULL;
10918 ret = nmc_rl_gen_func_basic (text, state, connections);
10920 g_free (connections);
10925 nmcli_con_tab_completion (const char *text, int start, int end)
10927 char **match_array = NULL;
10928 rl_compentry_func_t *generator_func = NULL;
10930 /* Disable readline's default filename completion */
10931 rl_attempted_completion_over = 1;
10933 if (g_strcmp0 (rl_prompt, PROMPT_CONNECTION) == 0) {
10934 /* Disable appending space after completion */
10935 rl_completion_append_character = '\0';
10937 if (!is_single_word (rl_line_buffer))
10940 generator_func = gen_func_connection_names;
10941 } else if (g_strcmp0 (rl_prompt, PROMPT_CONNECTIONS) == 0) {
10942 generator_func = gen_func_connection_names;
10943 } else if (g_strcmp0 (rl_prompt, PROMPT_ACTIVE_CONNECTIONS) == 0) {
10944 generator_func = gen_func_active_connection_names;
10945 } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_TYPE) == 0) {
10946 generator_func = gen_func_vpn_types;
10947 } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_FILE) == 0) {
10948 rl_attempted_completion_over = 0;
10949 rl_complete_with_tilde_expansion = 1;
10950 } else if (g_strcmp0 (rl_prompt, PROMPT_VPN_CONNECTION) == 0) {
10951 generator_func = gen_vpn_ids;
10954 if (generator_func)
10955 match_array = rl_completion_matches (text, generator_func);
10957 return match_array;
10961 parse_preferred_connection_order (const char *order, GError **error)
10963 char **strv, **iter;
10967 gboolean inverse, unique;
10970 strv = nmc_strsplit_set (order, ":", -1);
10971 if (!strv || !*strv) {
10972 g_set_error (error, NMCLI_ERROR, 0,
10973 _("incorrect string '%s' of '--order' option"), order);
10978 order_arr = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), 4);
10979 for (iter = strv; iter && *iter; iter++) {
10984 if (str[0] == '+' || str[0] == '-')
10987 if (matches (str, "active") == 0)
10988 val = inverse ? NMC_SORT_ACTIVE_INV : NMC_SORT_ACTIVE;
10989 else if (matches (str, "name") == 0)
10990 val = inverse ? NMC_SORT_NAME_INV : NMC_SORT_NAME;
10991 else if (matches (str, "type") == 0)
10992 val = inverse ? NMC_SORT_TYPE_INV : NMC_SORT_TYPE;
10993 else if (matches (str, "path") == 0)
10994 val = inverse ? NMC_SORT_PATH_INV : NMC_SORT_PATH;
10996 g_array_unref (order_arr);
10998 g_set_error (error, NMCLI_ERROR, 0,
10999 _("incorrect item '%s' in '--order' option"), *iter);
11002 /* Check for duplicates and ignore them. */
11004 for (i = 0; i < order_arr->len; i++) {
11005 if (abs (g_array_index (order_arr, NmcSortOrder, i)) - abs (val) == 0) {
11011 /* Value is ok and unique, add it to the array */
11013 g_array_append_val (order_arr, val);
11020 /* Entry point function for connections-related commands: 'nmcli connection' */
11022 do_connections (NmCli *nmc, int argc, char **argv)
11024 GError *error = NULL;
11026 /* Register polkit agent */
11027 nmc_start_polkit_agent_start_try (nmc);
11029 /* Set completion function for 'nmcli con' */
11030 rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_tab_completion;
11032 /* Exit early on help */
11033 if (nmc_arg_is_help (*argv)) {
11035 return nmc->return_value;
11037 if (argc != 0 && nmc_arg_is_help (*(argv+1))) {
11038 if (usage_connection_second_level (*argv))
11039 return nmc->return_value;
11042 /* Get NMClient object early */
11043 nmc->get_client (nmc);
11045 /* Check whether NetworkManager is running */
11046 if (!nm_client_get_nm_running (nmc->client)) {
11047 g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
11048 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
11049 return nmc->return_value;
11051 /* Compare NM and nmcli versions */
11052 if (!nmc_versions_match (nmc))
11053 return nmc->return_value;
11055 /* Get the connection list */
11056 nmc->connections = nm_client_get_connections (nmc->client);
11058 /* Now parse the command line and perform the required operation */
11060 if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
11062 nmc->return_value = do_connections_show (nmc, FALSE, FALSE, NULL, argc, argv);
11064 if (matches (*argv, "show") == 0) {
11065 gboolean active = FALSE;
11066 gboolean show_secrets = FALSE;
11067 GArray *order = NULL;
11070 next_arg (&argc, &argv);
11071 /* check connection show options [--active] [--show-secrets] */
11072 for (i = 0; i < 3; i++) {
11073 if (!active && nmc_arg_is_option (*argv, "active")) {
11075 next_arg (&argc, &argv);
11077 /* --show-secrets is deprecated in favour of global --show-secrets */
11078 /* Keep it here for backwards compatibility */
11079 if (!show_secrets && nmc_arg_is_option (*argv, "show-secrets")) {
11080 show_secrets = TRUE;
11081 next_arg (&argc, &argv);
11083 if (!order && nmc_arg_is_option (*argv, "order")) {
11084 if (next_arg (&argc, &argv) != 0) {
11085 g_set_error_literal (&error, NMCLI_ERROR, 0,
11086 _("'--order' argument is missing"));
11089 order = parse_preferred_connection_order (*argv, &error);
11092 next_arg (&argc, &argv);
11095 show_secrets = nmc->show_secrets || show_secrets;
11096 nmc->return_value = do_connections_show (nmc, active, show_secrets, order, argc, argv);
11098 g_array_unref (order);
11099 } else if (matches(*argv, "up") == 0) {
11100 nmc->return_value = do_connection_up (nmc, argc-1, argv+1);
11101 } else if (matches(*argv, "down") == 0) {
11102 nmc->return_value = do_connection_down (nmc, argc-1, argv+1);
11103 } else if (matches(*argv, "add") == 0) {
11104 nmc->return_value = do_connection_add (nmc, argc-1, argv+1);
11105 } else if (matches(*argv, "edit") == 0) {
11106 nmc->should_wait++;
11107 editor_thread_data.nmc = nmc;
11108 editor_thread_data.argc = argc - 1;
11109 editor_thread_data.argv = argv + 1;
11110 editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data);
11111 g_thread_unref (editor_thread);
11112 } else if (matches(*argv, "delete") == 0) {
11113 nmc->return_value = do_connection_delete (nmc, argc-1, argv+1);
11114 } else if (matches(*argv, "reload") == 0) {
11115 nmc->return_value = do_connection_reload (nmc, argc-1, argv+1);
11116 } else if (matches(*argv, "load") == 0) {
11117 nmc->return_value = do_connection_load (nmc, argc-1, argv+1);
11118 } else if (matches (*argv, "modify") == 0) {
11119 gboolean temporary = FALSE;
11121 next_arg (&argc, &argv);
11122 if (nmc_arg_is_option (*argv, "temporary")) {
11124 next_arg (&argc, &argv);
11126 nmc->return_value = do_connection_modify (nmc, temporary, argc, argv);
11127 } else if (matches (*argv, "clone") == 0) {
11128 gboolean temporary = FALSE;
11130 next_arg (&argc, &argv);
11131 if (nmc_arg_is_option (*argv, "temporary")) {
11133 next_arg (&argc, &argv);
11135 nmc->return_value = do_connection_clone (nmc, temporary, argc, argv);
11136 } else if (matches(*argv, "import") == 0) {
11137 gboolean temporary = FALSE;
11139 next_arg (&argc, &argv);
11140 if (nmc_arg_is_option (*argv, "temporary")) {
11142 next_arg (&argc, &argv);
11144 nmc->return_value = do_connection_import (nmc, temporary, argc, argv);
11145 } else if (matches(*argv, "export") == 0) {
11146 nmc->return_value = do_connection_export (nmc, argc-1, argv+1);
11147 } else if (matches(*argv, "monitor") == 0) {
11148 nmc->return_value = do_connection_monitor (nmc, argc-1, argv+1);
11151 g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv);
11152 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
11156 return nmc->return_value;
11159 g_string_printf (nmc->return_text, _("Error: %s."), error->message);
11160 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
11161 g_error_free (error);
11162 return nmc->return_value;
11166 monitor_connections (NmCli *nmc)
11168 do_connection_monitor (nmc, 0, NULL);