1 /* nmcli - command-line tool to control NetworkManager
2 *
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.
7 *
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.
12 *
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.
16 *
17 * (C) Copyright 2010 - 2013 Red Hat, Inc.
18 */
19
20 #include "config.h"
21
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <dbus/dbus.h>
25 #include <dbus/dbus-glib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <netinet/ether.h>
33
34 #include <nm-client.h>
35 #include <nm-device-ethernet.h>
36 #include <nm-device-adsl.h>
37 #include <nm-device-wifi.h>
38 #if WITH_WIMAX
39 #include <nm-device-wimax.h>
40 #endif
41 #include <nm-device-modem.h>
42 #include <nm-device-bt.h>
43 #include <nm-device-olpc-mesh.h>
44 #include <nm-device-infiniband.h>
45 #include <nm-device-bond.h>
46 #include <nm-device-team.h>
47 #include <nm-device-bridge.h>
48 #include <nm-device-vlan.h>
49 #include <nm-remote-settings.h>
50 #include <nm-vpn-connection.h>
51 #include <nm-utils.h>
52
53 #include "utils.h"
54 #include "common.h"
55 #include "settings.h"
56 #include "connections.h"
57
58 /* Activation timeout waiting for bond/team/bridge slaves (in seconds) */
59 #define SLAVES_UP_TIMEOUT 10
60
61 /* define some prompts for connection editor */
62 #define EDITOR_PROMPT_SETTING _("Setting name? ")
63 #define EDITOR_PROMPT_PROPERTY _("Property name? ")
64 #define EDITOR_PROMPT_CON_TYPE _("Enter connection type: ")
65
66 /* Available fields for 'connection show configured' */
67 static NmcOutputField nmc_fields_con_show[] = {
68 {"NAME", N_("NAME"), 25}, /* 0 */
69 {"UUID", N_("UUID"), 38}, /* 1 */
70 {"TYPE", N_("TYPE"), 17}, /* 2 */
71 {"TIMESTAMP", N_("TIMESTAMP"), 12}, /* 3 */
72 {"TIMESTAMP-REAL", N_("TIMESTAMP-REAL"), 34}, /* 4 */
73 {"AUTOCONNECT", N_("AUTOCONNECT"), 13}, /* 5 */
74 {"READONLY", N_("READONLY"), 10}, /* 6 */
75 {"DBUS-PATH", N_("DBUS-PATH"), 42}, /* 7 */
76 {NULL, NULL, 0}
77 };
78 #define NMC_FIELDS_CON_SHOW_ALL "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,READONLY,DBUS-PATH"
79 #define NMC_FIELDS_CON_SHOW_COMMON "NAME,UUID,TYPE,TIMESTAMP-REAL"
80
81 /* Helper macro to define fields */
82 #define SETTING_FIELD(setting, width) { setting, N_(setting), width, NULL, FALSE, FALSE, 0 }
83
84 /* Available settings for 'connection show configured <con>' */
85 static NmcOutputField nmc_fields_settings_names[] = {
86 SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME, 0), /* 0 */
87 SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME, 0), /* 1 */
88 SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME, 0), /* 2 */
89 SETTING_FIELD (NM_SETTING_WIRELESS_SETTING_NAME, 0), /* 3 */
90 SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, 0), /* 4 */
91 SETTING_FIELD (NM_SETTING_IP4_CONFIG_SETTING_NAME, 0), /* 5 */
92 SETTING_FIELD (NM_SETTING_IP6_CONFIG_SETTING_NAME, 0), /* 6 */
93 SETTING_FIELD (NM_SETTING_SERIAL_SETTING_NAME, 0), /* 7 */
94 SETTING_FIELD (NM_SETTING_PPP_SETTING_NAME, 0), /* 8 */
95 SETTING_FIELD (NM_SETTING_PPPOE_SETTING_NAME, 0), /* 9 */
96 SETTING_FIELD (NM_SETTING_GSM_SETTING_NAME, 0), /* 10 */
97 SETTING_FIELD (NM_SETTING_CDMA_SETTING_NAME, 0), /* 11 */
98 SETTING_FIELD (NM_SETTING_BLUETOOTH_SETTING_NAME, 0), /* 12 */
99 SETTING_FIELD (NM_SETTING_OLPC_MESH_SETTING_NAME, 0), /* 13 */
100 SETTING_FIELD (NM_SETTING_VPN_SETTING_NAME, 0), /* 14 */
101 SETTING_FIELD (NM_SETTING_WIMAX_SETTING_NAME, 0), /* 15 */
102 SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME, 0), /* 16 */
103 SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME, 0), /* 17 */
104 SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME, 0), /* 18 */
105 SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME, 0), /* 19 */
106 SETTING_FIELD (NM_SETTING_BRIDGE_SETTING_NAME, 0), /* 20 */
107 SETTING_FIELD (NM_SETTING_BRIDGE_PORT_SETTING_NAME, 0), /* 21 */
108 SETTING_FIELD (NM_SETTING_TEAM_SETTING_NAME, 0), /* 22 */
109 SETTING_FIELD (NM_SETTING_TEAM_PORT_SETTING_NAME, 0), /* 23 */
110 {NULL, NULL, 0, NULL, FALSE, FALSE, 0}
111 };
112 #define NMC_FIELDS_SETTINGS_NAMES_ALL_X NM_SETTING_CONNECTION_SETTING_NAME","\
113 NM_SETTING_WIRED_SETTING_NAME","\
114 NM_SETTING_802_1X_SETTING_NAME","\
115 NM_SETTING_WIRELESS_SETTING_NAME","\
116 NM_SETTING_WIRELESS_SECURITY_SETTING_NAME","\
117 NM_SETTING_IP4_CONFIG_SETTING_NAME","\
118 NM_SETTING_IP6_CONFIG_SETTING_NAME","\
119 NM_SETTING_SERIAL_SETTING_NAME","\
120 NM_SETTING_PPP_SETTING_NAME","\
121 NM_SETTING_PPPOE_SETTING_NAME","\
122 NM_SETTING_ADSL_SETTING_NAME","\
123 NM_SETTING_GSM_SETTING_NAME","\
124 NM_SETTING_CDMA_SETTING_NAME","\
125 NM_SETTING_BLUETOOTH_SETTING_NAME","\
126 NM_SETTING_OLPC_MESH_SETTING_NAME","\
127 NM_SETTING_VPN_SETTING_NAME","\
128 NM_SETTING_INFINIBAND_SETTING_NAME","\
129 NM_SETTING_BOND_SETTING_NAME","\
130 NM_SETTING_VLAN_SETTING_NAME","\
131 NM_SETTING_BRIDGE_SETTING_NAME","\
132 NM_SETTING_BRIDGE_PORT_SETTING_NAME","\
133 NM_SETTING_TEAM_SETTING_NAME","\
134 NM_SETTING_TEAM_PORT_SETTING_NAME
135 #if WITH_WIMAX
136 #define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X","\
137 NM_SETTING_WIMAX_SETTING_NAME
138 #else
139 #define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X
140 #endif
141
142
143 /* Available fields for 'connection show active' */
144 static NmcOutputField nmc_fields_con_show_active[] = {
145 {"GROUP", N_("GROUP"), 9}, /* 0 */ /* used only for 'GENERAL' group listing */
146 {"NAME", N_("NAME"), 25}, /* 1 */
147 {"UUID", N_("UUID"), 38}, /* 2 */
148 {"DEVICES", N_("DEVICES"), 10}, /* 3 */
149 {"STATE", N_("STATE"), 12}, /* 4 */
150 {"DEFAULT", N_("DEFAULT"), 8}, /* 5 */
151 {"DEFAULT6", N_("DEFAULT6"), 9}, /* 6 */
152 {"SPEC-OBJECT", N_("SPEC-OBJECT"), 10}, /* 7 */
153 {"VPN", N_("VPN"), 5}, /* 8 */
154 {"DBUS-PATH", N_("DBUS-PATH"), 51}, /* 9 */
155 {"CON-PATH", N_("CON-PATH"), 44}, /* 10 */
156 {"ZONE", N_("ZONE"), 15}, /* 11 */
157 {"MASTER-PATH", N_("MASTER-PATH"), 44}, /* 12 */
158 {NULL, NULL, 0}
159 };
160 #define NMC_FIELDS_CON_ACTIVE_ALL "NAME,UUID,DEVICES,STATE,DEFAULT,DEFAULT6,VPN,ZONE,DBUS-PATH,CON-PATH,SPEC-OBJECT,MASTER-PATH"
161 #define NMC_FIELDS_CON_ACTIVE_COMMON "NAME,UUID,DEVICES,DEFAULT,VPN,MASTER-PATH"
162
163 /* Available fields for 'connection show active <con>' */
164 static NmcOutputField nmc_fields_con_active_details_groups[] = {
165 {"GENERAL", N_("GENERAL"), 9}, /* 0 */
166 {"IP", N_("IP"), 5}, /* 1 */
167 {"VPN", N_("VPN"), 5}, /* 2 */
168 {NULL, NULL, 0}
169 };
170 #define NMC_FIELDS_CON_ACTIVE_DETAILS_ALL "GENERAL,IP,VPN"
171
172 /* GENERAL group is the same as nmc_fields_con_show_active */
173 #define NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL "GROUP,"NMC_FIELDS_CON_ACTIVE_ALL
174
175 /* IP group is handled by common.c */
176
177 /* Available fields for VPN group */
178 static NmcOutputField nmc_fields_con_active_details_vpn[] = {
179 {"GROUP", N_("GROUP"), 9}, /* 0 */
180 {"TYPE", N_("TYPE"), 15}, /* 1 */
181 {"USERNAME", N_("USERNAME"), 15}, /* 2 */
182 {"GATEWAY", N_("GATEWAY"), 25}, /* 3 */
183 {"BANNER", N_("BANNER"), 120}, /* 4 */
184 {"VPN-STATE", N_("VPN-STATE"), 40}, /* 5 */
185 {"CFG", N_("CFG"), 120}, /* 6 */
186 {NULL, NULL, 0}
187 };
188 #define NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG"
189
190
191 typedef struct {
192 NmCli *nmc;
193 int argc;
194 char **argv;
195 } ArgsInfo;
196
197 /* glib main loop variable - defined in nmcli.c */
198 extern GMainLoop *loop;
199
200 static ArgsInfo args_info;
201 static guint progress_id = 0; /* ID of event source for displaying progress */
202
203 /* for readline TAB completion */
204 static const char *nmc_completion_con_type = NULL;
205 static NMSetting *nmc_completion_setting = NULL;
206
207 static void
208 usage (void)
209 {
210 fprintf (stderr,
211 _("Usage: nmcli connection { COMMAND | help }\n"
212 " COMMAND := { show | up | down | delete }\n\n"
213 " show configured [[ id | uuid | path ] <ID>]\n\n"
214 " show active [[ id | uuid | path | apath ] <ID>]\n\n"
215 #if WITH_WIMAX
216 " up [ id | uuid | path ] <ID> [ifname <ifname>] [ap <BSSID>] [nsp <name>]\n\n"
217 #else
218 " up [ id | uuid | path ] <ID> [ifname <ifname>] [ap <BSSID>]\n\n"
219 #endif
220 " down [ id | uuid | path | apath ] <ID>\n\n"
221 " add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n"
222 " modify [ id | uuid | path ] <ID> <setting>.<property> <value>\n\n"
223 " edit [ id | uuid | path ] <ID> | [type <new_con_type>] [con-name <new_con_name>]\n\n"
224 " delete [ id | uuid | path ] <ID>\n\n"
225 " reload\n\n\n"
226 ));
227 }
228
229 static void
230 usage_connection_add (void)
231 {
232 fprintf (stderr,
233 _("Usage: nmcli connection add { OPTIONS | help }\n"
234 " OPTIONS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n"
235 " COMMON_OPTIONS:\n"
236 " type <type>\n"
237 " ifname <interface name> | \"*\"\n"
238 " [con-name <connection name>]\n"
239 " [autoconnect yes|no]\n\n"
240 " TYPE_SPECIFIC_OPTIONS:\n"
241 " ethernet: [mac <MAC address>]\n"
242 " [cloned-mac <cloned MAC address>]\n"
243 " [mtu <MTU>]\n\n"
244 " wifi: ssid <SSID>\n"
245 " [mac <MAC address>]\n"
246 " [cloned-mac <cloned MAC address>]\n"
247 " [mtu <MTU>]\n\n"
248 " wimax: [mac <MAC address>]\n"
249 " [nsp <NSP>]\n\n"
250 " gsm: apn <APN>\n"
251 " [user <username>]\n"
252 " [password <password>]\n\n"
253 " cdma: [user <username>]\n"
254 " [password <password>]\n\n"
255 " infiniband: [mac <MAC address>]\n"
256 " [mtu <MTU>]\n"
257 " [transport-mode datagram | connected]\n"
258 " [parent <ifname>]\n"
259 " [p-key <IPoIB P_Key>]\n\n"
260 " bluetooth: [addr <bluetooth address>]\n"
261 " [bt-type panu|dun-gsm|dun-cdma]\n\n"
262 " vlan: dev <parent device (connection UUID, ifname, or MAC)>\n"
263 " [id <VLAN id>]\n"
264 " [flags <VLAN flags>]\n"
265 " [ingress <ingress priority mapping>]\n"
266 " [egress <egress priority mapping>]\n"
267 " [mtu <MTU>]\n\n"
268 " bond: [mode balance-rr (0) | active-backup (1) | balance-xor (2) | broadcast (3) |\n"
269 " 802.3ad (4) | balance-tlb (5) | balance-alb (6)]\n"
270 " [primary <ifname>]\n"
271 " [miimon <num>]\n"
272 " [downdelay <num>]\n"
273 " [updelay <num>]\n"
274 " [arp-interval <num>]\n"
275 " [arp-ip-target <num>]\n\n"
276 " bond-slave: master <master (ifname or connection UUID)>\n\n"
277 " team: [config <json config>]\n\n"
278 " team-slave: master <master (ifname or connection UUID)>\n"
279 " [config <json config>]\n\n"
280 " bridge: [stp yes|no>]\n"
281 " [priority <num>]\n"
282 " [forward-delay <2-30>]\n"
283 " [hello-time <1-10>]\n"
284 " [max-age <6-40>]\n"
285 " [ageing-time <0-1000000>]\n\n"
286 " bridge-slave: master <master (ifname or connection UUID)\n"
287 " [priority <0-63>]\n"
288 " [path-cost <1-65535>]\n"
289 " [hairpin yes|no]\n\n"
290 " vpn: vpn-type vpnc|openvpn|pptp|openconnect|openswan\n"
291 " [user <username>]\n\n"
292 " olpc-mesh: ssid <SSID>\n"
293 " [channel <1-13>]\n"
294 " [dhcp-anycast <MAC address>]\n\n"
295 " IP_OPTIONS:\n"
296 " [ip4 <IPv4 address>] [gw4 <IPv4 gateway>]\n"
297 " [ip6 <IPv6 address>] [gw6 <IPv6 gateway>]\n"
298 ));
299 }
300
301 /* The real commands that do something - i.e. not 'help', etc. */
302 static const char *real_con_commands[] = {
303 "show",
304 "up",
305 "down",
306 "add",
307 "modify",
308 "edit",
309 "delete",
310 "reload",
311 NULL
312 };
313
314 /* quit main loop */
315 static void
316 quit (void)
317 {
318 if (progress_id) {
319 g_source_remove (progress_id);
320 nmc_terminal_erase_line ();
321 }
322
323 g_main_loop_quit (loop); /* quit main loop */
324 }
325
326 static gboolean
327 nmc_connection_detail (NMConnection *connection, NmCli *nmc)
328 {
329 GError *error = NULL;
330 GArray *print_settings_array;
331 int i;
332 char *fields_str;
333 char *fields_all = NMC_FIELDS_SETTINGS_NAMES_ALL;
334 char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL;
335 gboolean was_output = FALSE;
336
337 if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
338 fields_str = fields_common;
339 else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
340 fields_str = fields_all;
341 else
342 fields_str = nmc->required_fields;
343
344 print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, &error);
345 if (error) {
346 if (error->code == 0)
347 g_string_printf (nmc->return_text, _("Error: 'list configured': %s"), error->message);
348 else
349 g_string_printf (nmc->return_text, _("Error: 'list configured': %s; allowed fields: %s"),
350 error->message, NMC_FIELDS_SETTINGS_NAMES_ALL);
351 g_error_free (error);
352 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
353 return FALSE;
354 }
355
356 /* Main header */
357 nmc->print_fields.header_name = _("Connection details");
358 nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL,
359 nmc_fields_settings_names, NULL);
360
361 nmc_fields_settings_names[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
362 print_required_fields (nmc, nmc_fields_settings_names);
363
364 /* Loop through the required settings and print them. */
365 for (i = 0; i < print_settings_array->len; i++) {
366 NMSetting *setting;
367 int section_idx = g_array_index (print_settings_array, int, i);
368
369 if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
370 printf ("\n"); /* Empty line */
371
372 was_output = FALSE;
373
374 /* Remove any previous data */
375 nmc_empty_output_fields (nmc);
376
377 setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name);
378 if (setting) {
379 setting_details (setting, nmc);
380 was_output = TRUE;
381 continue;
382 }
383 }
384
385 if (print_settings_array)
386 g_array_free (print_settings_array, TRUE);
387
388 return TRUE;
389 }
390
391 static void
392 fill_output_connection (gpointer data, gpointer user_data)
393 {
394 NMConnection *connection = (NMConnection *) data;
395 NmCli *nmc = (NmCli *) user_data;
396 NMSettingConnection *s_con;
397 guint64 timestamp;
398 time_t timestamp_real;
399 char *timestamp_str;
400 char *timestamp_real_str;
401 NmcOutputField *arr;
402
403 s_con = nm_connection_get_setting_connection (connection);
404 if (s_con) {
405 /* Obtain field values */
406 timestamp = nm_setting_connection_get_timestamp (s_con);
407 timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
408 if (timestamp) {
409 timestamp_real = timestamp;
410 timestamp_real_str = g_malloc0 (64);
411 strftime (timestamp_real_str, 64, "%c", localtime (×tamp_real));
412 }
413
414 arr = nmc_dup_fields_array (nmc_fields_con_show,
415 sizeof (nmc_fields_con_show),
416 0);
417 set_val_strc (arr, 0, nm_setting_connection_get_id (s_con));
418 set_val_strc (arr, 1, nm_setting_connection_get_uuid (s_con));
419 set_val_strc (arr, 2, nm_setting_connection_get_connection_type (s_con));
420 set_val_str (arr, 3, timestamp_str);
421 set_val_str (arr, 4, timestamp ? timestamp_real_str : g_strdup (_("never")));
422 set_val_strc (arr, 5, nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no"));
423 set_val_strc (arr, 6, nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no"));
424 set_val_strc (arr, 7, nm_connection_get_path (connection));
425
426 g_ptr_array_add (nmc->output_data, arr);
427 }
428 }
429
430 static NMConnection *
431 find_connection (GSList *list, const char *filter_type, const char *filter_val)
432 {
433 NMConnection *connection;
434 GSList *iterator;
435 const char *id;
436 const char *uuid;
437 const char *path, *path_num;
438
439 iterator = list;
(1) Event cond_true: |
Condition "iterator", taking true branch |
(13) Event loop_begin: |
Jumped back to beginning of loop |
(14) Event cond_true: |
Condition "iterator", taking true branch |
440 while (iterator) {
441 connection = NM_CONNECTION (iterator->data);
442
443 id = nm_connection_get_id (connection);
444 uuid = nm_connection_get_uuid (connection);
445 path = nm_connection_get_path (connection);
(2) Event cond_true: |
Condition "path", taking true branch |
(15) Event cond_false: |
Condition "path", taking false branch |
(16) Event var_compare_op: |
Comparing "path" to null implies that "path" might be null. |
Also see events: |
[var_deref_model] |
446 path_num = path ? strrchr (path, '/') + 1 : NULL;
447
448 /* When filter_type is NULL, compare connection ID (filter_val)
449 * against all types. Otherwise, only compare against the specific
450 * type. If 'path' filter type is specified, comparison against
451 * numeric index (in addition to the whole path) is allowed.
452 */
(3) Event cond_true: |
Condition "!filter_type", taking true branch |
(4) Event cond_false: |
Condition "__coverity_strcmp(filter_val, id) == 0", taking false branch |
(5) Event cond_true: |
Condition "!filter_type", taking true branch |
(6) Event cond_false: |
Condition "__coverity_strcmp(filter_val, uuid) == 0", taking false branch |
(7) Event cond_true: |
Condition "!filter_type", taking true branch |
(8) Event cond_false: |
Condition "__coverity_strcmp(filter_val, path) == 0", taking false branch |
(9) Event cond_false: |
Condition "filter_type", taking false branch |
(17) Event cond_true: |
Condition "!filter_type", taking true branch |
(18) Event cond_false: |
Condition "__coverity_strcmp(filter_val, id) == 0", taking false branch |
(19) Event cond_true: |
Condition "!filter_type", taking true branch |
(20) Event cond_false: |
Condition "__coverity_strcmp(filter_val, uuid) == 0", taking false branch |
(21) Event cond_true: |
Condition "!filter_type", taking true branch |
(22) Event var_deref_model: |
Passing null pointer "path" to function "__coverity_strcmp(char const *, char const *)", which dereferences it. |
Also see events: |
[var_compare_op] |
453 if ( ( (!filter_type || strcmp (filter_type, "id") == 0)
454 && strcmp (filter_val, id) == 0)
455 || ( (!filter_type || strcmp (filter_type, "uuid") == 0)
456 && strcmp (filter_val, uuid) == 0)
457 || ( (!filter_type || strcmp (filter_type, "path") == 0)
458 && (strcmp (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0))))
(10) Event if_end: |
End of if statement |
459 return connection;
460
(11) Event cond_true: |
Condition "iterator", taking true branch |
461 iterator = g_slist_next (iterator);
(12) Event loop: |
Jumping back to the beginning of the loop |
462 }
463
464 return NULL;
465 }
466
467 static NMCResultCode
468 do_connections_show (NmCli *nmc, int argc, char **argv)
469 {
470 GError *error1 = NULL;
471 GError *error2 = NULL;
472 char *fields_str;
473 char *fields_all = NMC_FIELDS_CON_SHOW_ALL;
474 char *fields_common = NMC_FIELDS_CON_SHOW_COMMON;
475 gboolean printed = FALSE;
476
477 nmc->should_wait = FALSE;
478
479 if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
480 fields_str = fields_common;
481 else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
482 fields_str = fields_all;
483 else
484 fields_str = nmc->required_fields;
485
486 if (argc == 0) {
487 NmcOutputField *tmpl, *arr;
488 size_t tmpl_len;
489
490 tmpl = nmc_fields_con_show;
491 tmpl_len = sizeof (nmc_fields_con_show);
492 nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, &error1);
493 if (error1)
494 goto error;
495 if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error2))
496 goto error;
497
498 /* Add headers */
499 nmc->print_fields.header_name = _("List of configured connections");
500 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES);
501 g_ptr_array_add (nmc->output_data, arr);
502
503 /* Add values */
504 g_slist_foreach (nmc->system_connections, fill_output_connection, nmc);
505 print_data (nmc); /* Print all data */
506 } else {
507 while (argc > 0) {
508 NMConnection *con;
509 const char *selector = NULL;
510
511 if ( strcmp (*argv, "id") == 0
512 || strcmp (*argv, "uuid") == 0
513 || strcmp (*argv, "path") == 0) {
514 selector = *argv;
515 if (next_arg (&argc, &argv) != 0) {
516 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
517 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
518 return nmc->return_value;
519 }
520 }
521 if (!nmc->mode_specified)
522 nmc->multiline_output = TRUE; /* multiline mode is default for 'show configured <con>' */
523
524 con = find_connection (nmc->system_connections, selector, *argv);
525 if (con) {
526 if (printed)
527 printf ("\n");
528 printed = nmc_connection_detail (con, nmc);
529 } else {
530 g_string_printf (nmc->return_text, _("Error: %s - no such connection."), *argv);
531 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
532 return nmc->return_value;
533 }
534
535 argc--;
536 argv++;
537 }
538 }
539
540 error:
541 if (error1) {
542 if (error1->code == 0)
543 g_string_printf (nmc->return_text, _("Error: 'show configured': %s"), error1->message);
544 else
545 g_string_printf (nmc->return_text, _("Error: 'show configured': %s; allowed fields: %s"),
546 error1->message, NMC_FIELDS_CON_SHOW_ALL);
547 g_error_free (error1);
548 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
549 }
550 if (error2) {
551 g_string_printf (nmc->return_text, _("Error: %s."), error2->message);
552 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
553 g_error_free (error2);
554 }
555
556 return nmc->return_value;
557 }
558
559 static const char *
560 active_connection_state_to_string (NMActiveConnectionState state)
561 {
562 switch (state) {
563 case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
564 return _("activating");
565 case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
566 return _("activated");
567 case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING:
568 return _("deactivating");
569 case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED:
570 return _("deactivated");
571 case NM_ACTIVE_CONNECTION_STATE_UNKNOWN:
572 default:
573 return _("unknown");
574 }
575 }
576
577 static const char *
578 vpn_connection_state_to_string (NMVPNConnectionState state)
579 {
580 switch (state) {
581 case NM_VPN_CONNECTION_STATE_PREPARE:
582 return _("VPN connecting (prepare)");
583 case NM_VPN_CONNECTION_STATE_NEED_AUTH:
584 return _("VPN connecting (need authentication)");
585 case NM_VPN_CONNECTION_STATE_CONNECT:
586 return _("VPN connecting");
587 case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
588 return _("VPN connecting (getting IP configuration)");
589 case NM_VPN_CONNECTION_STATE_ACTIVATED:
590 return _("VPN connected");
591 case NM_VPN_CONNECTION_STATE_FAILED:
592 return _("VPN connection failed");
593 case NM_VPN_CONNECTION_STATE_DISCONNECTED:
594 return _("VPN disconnected");
595 default:
596 return _("unknown");
597 }
598 }
599
600 static NMConnection *
601 get_connection_for_active (const GSList *con_list, NMActiveConnection *active)
602 {
603 const GSList *iter;
604 const char *path;
605
606 path = nm_active_connection_get_connection (active);
607 g_return_val_if_fail (path != NULL, NULL);
608
609 for (iter = con_list; iter; iter = g_slist_next (iter)) {
610 NMConnection *candidate = NM_CONNECTION (iter->data);
611
612 if (strcmp (nm_connection_get_path (candidate), path) == 0)
613 return candidate;
614 }
615 return NULL;
616 }
617
618 static void
619 fill_output_active_connection (NMActiveConnection *active,
620 NmCli *nmc,
621 gboolean with_group,
622 guint32 o_flags)
623 {
624 GSList *iter;
625 const char *active_path;
626 NMSettingConnection *s_con;
627 const GPtrArray *devices;
628 GString *dev_str;
629 NMActiveConnectionState state;
630 int i;
631 GSList *con_list = nmc->system_connections;
632 NmcOutputField *tmpl, *arr;
633 size_t tmpl_len;
634 int idx_start = with_group ? 0 : 1;
635
636 active_path = nm_active_connection_get_connection (active);
637 state = nm_active_connection_get_state (active);
638
639 /* Get devices of the active connection */
640 dev_str = g_string_new (NULL);
641 devices = nm_active_connection_get_devices (active);
642 for (i = 0; devices && (i < devices->len); i++) {
643 NMDevice *device = g_ptr_array_index (devices, i);
644 const char *dev_iface = nm_device_get_iface (device);
645
646 if (dev_iface) {
647 g_string_append (dev_str, dev_iface);
648 g_string_append_c (dev_str, ',');
649 }
650 }
651 if (dev_str->len > 0)
652 g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */
653
654 tmpl = nmc_fields_con_show_active;
655 tmpl_len = sizeof (nmc_fields_con_show_active);
656 if (!with_group) {
657 tmpl++;
658 tmpl_len -= sizeof (NmcOutputField);
659 }
660
661 /* Fill field values */
662 arr = nmc_dup_fields_array (tmpl, tmpl_len, o_flags);
663 if (with_group)
664 set_val_strc (arr, 0, nmc_fields_con_active_details_groups[0].name);
665 set_val_strc (arr, 1-idx_start, _("N/A"));
666 set_val_strc (arr, 2-idx_start, nm_active_connection_get_uuid (active));
667 set_val_str (arr, 3-idx_start, dev_str->str);
668 set_val_strc (arr, 4-idx_start, active_connection_state_to_string (state));
669 set_val_strc (arr, 5-idx_start, nm_active_connection_get_default (active) ? _("yes") : _("no"));
670 set_val_strc (arr, 6-idx_start, nm_active_connection_get_default6 (active) ? _("yes") : _("no"));
671 set_val_strc (arr, 7-idx_start, nm_active_connection_get_specific_object (active));
672 set_val_strc (arr, 8-idx_start, NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no"));
673 set_val_strc (arr, 9-idx_start, nm_object_get_path (NM_OBJECT (active)));
674 set_val_strc (arr, 10-idx_start, nm_active_connection_get_connection (active));
675 set_val_strc (arr, 11-idx_start, _("N/A"));
676 set_val_strc (arr, 12-idx_start, nm_active_connection_get_master (active));
677
678 for (iter = con_list; iter; iter = g_slist_next (iter)) {
679 NMConnection *connection = (NMConnection *) iter->data;
680 const char *con_path = nm_connection_get_path (connection);
681
682 if (!strcmp (active_path, con_path)) {
683 /* This connection is active */
684 s_con = nm_connection_get_setting_connection (connection);
685 g_assert (s_con != NULL);
686
687 /* Fill field values that depend on NMConnection */
688 set_val_strc (arr, 1-idx_start, nm_setting_connection_get_id (s_con));
689 set_val_strc (arr, 11-idx_start, nm_setting_connection_get_zone (s_con));
690
691 break;
692 }
693 }
694 g_ptr_array_add (nmc->output_data, arr);
695
696 g_string_free (dev_str, FALSE);
697 }
698
699 static NMActiveConnection *
700 find_active_connection (const GPtrArray *active_cons, const GSList *cons,
701 const char *filter_type, const char *filter_val)
702 {
703 int i;
704 const char *path, *a_path, *path_num, *a_path_num;
705 const char *id;
706 const char *uuid;
707 NMConnection *con;
708
709 for (i = 0; active_cons && (i < active_cons->len); i++) {
710 NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
711
712 path = nm_active_connection_get_connection (candidate);
713 a_path = nm_object_get_path (NM_OBJECT (candidate));
714 uuid = nm_active_connection_get_uuid (candidate);
715 path_num = path ? strrchr (path, '/') + 1 : NULL;
716 a_path_num = a_path ? strrchr (a_path, '/') + 1 : NULL;
717
718 con = get_connection_for_active (cons, candidate);
719 id = nm_connection_get_id (con);
720
721 /* When filter_type is NULL, compare connection ID (filter_val)
722 * against all types. Otherwise, only compare against the specific
723 * type. If 'path' or 'apath' filter types are specified, comparison
724 * against numeric index (in addition to the whole path) is allowed.
725 */
726 if ( ( (!filter_type || strcmp (filter_type, "id") == 0)
727 && strcmp (filter_val, id) == 0)
728 || ( (!filter_type || strcmp (filter_type, "uuid") == 0)
729 && strcmp (filter_val, uuid) == 0)
730 || ( (!filter_type || strcmp (filter_type, "path") == 0)
731 && (strcmp (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)))
732 || ( (!filter_type || strcmp (filter_type, "apath") == 0)
733 && (strcmp (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0))))
734 return candidate;
735 }
736 return NULL;
737 }
738
739 typedef struct {
740 char **array;
741 guint32 idx;
742 } FillVPNDataInfo;
743
744 static void
745 fill_vpn_data_item (const char *key, const char *value, gpointer user_data)
746 {
747 FillVPNDataInfo *info = (FillVPNDataInfo *) user_data;
748
749 info->array[info->idx++] = g_strdup_printf ("%s = %s", key, value);
750 }
751
752 // FIXME: The same or similar code for VPN info appears also in nm-applet (applet-dialogs.c),
753 // and in gnome-control-center as well. It could probably be shared somehow.
754 static char *
755 get_vpn_connection_type (NMConnection *connection)
756 {
757 const char *type, *p;
758
759 /* The service type is in form of "org.freedesktop.NetworkManager.vpnc".
760 * Extract end part after last dot, e.g. "vpnc"
761 */
762 type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
763 p = strrchr (type, '.');
764 return g_strdup (p ? p + 1 : type);
765 }
766
767 /* VPN parameters can be found at:
768 * http://git.gnome.org/browse/network-manager-openvpn/tree/src/nm-openvpn-service.h
769 * http://git.gnome.org/browse/network-manager-vpnc/tree/src/nm-vpnc-service.h
770 * http://git.gnome.org/browse/network-manager-pptp/tree/src/nm-pptp-service.h
771 * http://git.gnome.org/browse/network-manager-openconnect/tree/src/nm-openconnect-service.h
772 * http://git.gnome.org/browse/network-manager-openswan/tree/src/nm-openswan-service.h
773 * See also 'properties' directory in these plugins.
774 */
775 static const gchar *
776 find_vpn_gateway_key (const char *vpn_type)
777 {
778 if (g_strcmp0 (vpn_type, "openvpn") == 0) return "remote";
779 if (g_strcmp0 (vpn_type, "vpnc") == 0) return "IPSec gateway";
780 if (g_strcmp0 (vpn_type, "pptp") == 0) return "gateway";
781 if (g_strcmp0 (vpn_type, "openconnect") == 0) return "gateway";
782 if (g_strcmp0 (vpn_type, "openswan") == 0) return "right";
783 return "";
784 }
785
786 static const gchar *
787 find_vpn_username_key (const char *vpn_type)
788 {
789 if (g_strcmp0 (vpn_type, "openvpn") == 0) return "username";
790 if (g_strcmp0 (vpn_type, "vpnc") == 0) return "Xauth username";
791 if (g_strcmp0 (vpn_type, "pptp") == 0) return "user";
792 if (g_strcmp0 (vpn_type, "openconnect") == 0) return "username";
793 if (g_strcmp0 (vpn_type, "openswan") == 0) return "leftxauthusername";
794 return "";
795 }
796
797 enum VpnDataItem {
798 VPN_DATA_ITEM_GATEWAY,
799 VPN_DATA_ITEM_USERNAME
800 };
801
802 static const gchar *
803 get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item)
804 {
805 const char *key;
806 char *type = get_vpn_connection_type (connection);
807
808 switch (vpn_data_item) {
809 case VPN_DATA_ITEM_GATEWAY:
810 key = find_vpn_gateway_key (type);
811 break;
812 case VPN_DATA_ITEM_USERNAME:
813 key = find_vpn_username_key (type);
814 break;
815 default:
816 key = "";
817 break;
818 }
819 g_free (type);
820
821 return nm_setting_vpn_get_data_item (nm_connection_get_setting_vpn (connection), key);
822 }
823 /* FIXME end */
824
825 static gboolean
826 nmc_active_connection_detail (NMActiveConnection *acon, NmCli *nmc)
827 {
828 GError *error = NULL;
829 GArray *print_groups;
830 int i;
831 char *fields_str;
832 char *fields_all = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
833 char *fields_common = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
834 NmcOutputField *tmpl, *arr;
835 size_t tmpl_len;
836 gboolean was_output = FALSE;
837
838 if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
839 fields_str = fields_common;
840 else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
841 fields_str = fields_all;
842 else
843 fields_str = nmc->required_fields;
844
845 print_groups = parse_output_fields (fields_str, nmc_fields_con_active_details_groups, &error);
846 if (error) {
847 if (error->code == 0)
848 g_string_printf (nmc->return_text, _("Error: 'list active': %s"), error->message);
849 else
850 g_string_printf (nmc->return_text, _("Error: 'list active': %s; allowed fields: %s"),
851 error->message, NMC_FIELDS_CON_ACTIVE_DETAILS_ALL);
852 g_error_free (error);
853 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
854 return FALSE;
855 }
856
857 /* Main header */
858 nmc->print_fields.header_name = _("Active connection details");
859 nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_ALL,
860 nmc_fields_con_active_details_groups, NULL);
861
862 nmc_fields_con_active_details_groups[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
863 print_required_fields (nmc, nmc_fields_con_active_details_groups);
864
865 /* Loop through the groups and print them. */
866 for (i = 0; i < print_groups->len; i++) {
867 int group_idx = g_array_index (print_groups, int, i);
868
869 if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
870 printf ("\n"); /* Empty line */
871
872 was_output = FALSE;
873
874 /* Remove any previous data */
875 nmc_empty_output_fields (nmc);
876
877 /* GENERAL */
878 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[0].name) == 0) {
879 /* Add field names */
880 tmpl = nmc_fields_con_show_active;
881 tmpl_len = sizeof (nmc_fields_con_show_active);
882 nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL, tmpl, NULL);
883 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
884 g_ptr_array_add (nmc->output_data, arr);
885
886 /* Fill in values */
887 fill_output_active_connection (acon, nmc, TRUE, NMC_OF_FLAG_SECTION_PREFIX);
888
889 print_data (nmc); /* Print all data */
890
891 was_output = TRUE;
892 }
893
894 /* IP */
895 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[1].name) == 0) {
896 const GPtrArray *devices;
897 int j;
898
899 devices = nm_active_connection_get_devices (acon);
900 for (j = 0; devices && (j < devices->len); j++) {
901 gboolean b1 = FALSE, b2 = FALSE, b3 = FALSE, b4 = FALSE;
902 NMDevice *device = g_ptr_array_index (devices, j);
903 NMIP4Config *cfg4 = nm_device_get_ip4_config (device);
904 NMIP6Config *cfg6 = nm_device_get_ip6_config (device);
905 NMDHCP4Config *dhcp4 = nm_device_get_dhcp4_config (device);
906 NMDHCP6Config *dhcp6 = nm_device_get_dhcp6_config (device);
907
908 b1 = print_ip4_config (cfg4, nmc, "IP4");
909 b2 = print_dhcp4_config (dhcp4, nmc, "DHCP4");
910 b3 = print_ip6_config (cfg6, nmc, "IP6");
911 b4 = print_dhcp6_config (dhcp6, nmc, "DHCP6");
912 was_output = was_output || b1 || b2 || b3 || b4;
913 }
914 }
915
916 /* VPN */
917 if (NM_IS_VPN_CONNECTION (acon) &&
918 strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[2].name) == 0) {
919 NMConnection *con;
920 NMSettingConnection *s_con;
921 NMSettingVPN *s_vpn;
922 NMVPNConnectionState vpn_state;
923 char *type_str, *banner_str, *vpn_state_str;
924 const char *username = NULL;
925 char **vpn_data_array = NULL;
926 guint32 items_num;
927
928 con = get_connection_for_active (nmc->system_connections, acon);
929
930 s_con = nm_connection_get_setting_connection (con);
931 g_assert (s_con != NULL);
932
933 tmpl = nmc_fields_con_active_details_vpn;
934 tmpl_len = sizeof (nmc_fields_con_active_details_vpn);
935 nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL, tmpl, NULL);
936 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
937 g_ptr_array_add (nmc->output_data, arr);
938
939 s_vpn = nm_connection_get_setting_vpn (con);
940 if (s_vpn) {
941 items_num = nm_setting_vpn_get_num_data_items (s_vpn);
942 if (items_num > 0) {
943 FillVPNDataInfo info;
944
945 vpn_data_array = g_new (char *, items_num + 1);
946 info.array = vpn_data_array;
947 info.idx = 0;
948 nm_setting_vpn_foreach_data_item (s_vpn, &fill_vpn_data_item, &info);
949 vpn_data_array[items_num] = NULL;
950 }
951 username = nm_setting_vpn_get_user_name (s_vpn);
952 }
953
954 type_str = get_vpn_connection_type (con);
955 banner_str = g_strescape (nm_vpn_connection_get_banner (NM_VPN_CONNECTION (acon)), "");
956 vpn_state = nm_vpn_connection_get_vpn_state (NM_VPN_CONNECTION (acon));
957 vpn_state_str = g_strdup_printf ("%d - %s", vpn_state, vpn_connection_state_to_string (vpn_state));
958
959 /* Add values */
960 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX);
961 set_val_strc (arr, 0, nmc_fields_con_active_details_groups[2].name);
962 set_val_str (arr, 1, type_str);
963 set_val_strc (arr, 2, username ? username : get_vpn_data_item (con, VPN_DATA_ITEM_USERNAME));
964 set_val_strc (arr, 3, get_vpn_data_item (con, VPN_DATA_ITEM_GATEWAY));
965 set_val_str (arr, 4, banner_str);
966 set_val_str (arr, 5, vpn_state_str);
967 set_val_arr (arr, 6, vpn_data_array);
968 g_ptr_array_add (nmc->output_data, arr);
969
970 print_data (nmc); /* Print all data */
971 was_output = TRUE;
972 }
973 }
974
975 if (print_groups)
976 g_array_free (print_groups, TRUE);
977
978 return TRUE;
979 }
980
981 static NMCResultCode
982 do_connections_show_active (NmCli *nmc, int argc, char **argv)
983 {
984 const GPtrArray *active_cons;
985 int i;
986 GError *err1 = NULL;
987 gboolean printed = FALSE;
988 NmcOutputField *tmpl, *arr;
989 size_t tmpl_len;
990
991 nmc->should_wait = FALSE;
992
993 /* Get active connections */
994 nmc->get_client (nmc);
995
996 if (!nm_client_get_manager_running (nmc->client)) {
997 g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
998 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
999 goto error;
1000 }
1001
1002 active_cons = nm_client_get_active_connections (nmc->client);
1003
1004 if (argc == 0) {
1005 char *fields_str;
1006 char *fields_all = NMC_FIELDS_CON_ACTIVE_ALL;
1007 char *fields_common = NMC_FIELDS_CON_ACTIVE_COMMON;
1008
1009 if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
1010 fields_str = fields_common;
1011 else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
1012 fields_str = fields_all;
1013 else
1014 fields_str = nmc->required_fields;
1015
1016 tmpl = nmc_fields_con_show_active + 1;
1017 tmpl_len = sizeof (nmc_fields_con_show_active) - sizeof (NmcOutputField);
1018 nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, &err1);
1019 if (err1)
1020 goto error;
1021
1022 /* Add headers */
1023 nmc->print_fields.header_name = _("List of active connections");
1024 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES);
1025 g_ptr_array_add (nmc->output_data, arr);
1026
1027 /* Add values */
1028 for (i = 0; active_cons && i < active_cons->len; i++) {
1029 NMActiveConnection *ac = g_ptr_array_index (active_cons, i);
1030 fill_output_active_connection (ac, nmc, FALSE, 0);
1031 }
1032 print_data (nmc); /* Print all data */
1033 } else {
1034 while (argc > 0) {
1035 NMActiveConnection *acon;
1036 const char *selector = NULL;
1037
1038 if ( strcmp (*argv, "id") == 0
1039 || strcmp (*argv, "uuid") == 0
1040 || strcmp (*argv, "path") == 0
1041 || strcmp (*argv, "apath") == 0) {
1042
1043 selector = *argv;
1044 if (next_arg (&argc, &argv) != 0) {
1045 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
1046 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1047 return nmc->return_value;
1048 }
1049 }
1050 if (!nmc->mode_specified)
1051 nmc->multiline_output = TRUE; /* multiline mode is default for 'show active <con>' */
1052
1053 acon = find_active_connection (active_cons, nmc->system_connections, selector, *argv);
1054 if (acon) {
1055 if (printed)
1056 printf ("\n");
1057 printed = nmc_active_connection_detail (acon, nmc); /* separate connections by blank line */
1058 } else {
1059 g_string_printf (nmc->return_text, _("Error: '%s' is not an active connection."), *argv);
1060 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
1061 return nmc->return_value;
1062 }
1063
1064 argc--;
1065 argv++;
1066 }
1067 }
1068
1069 error:
1070 if (err1) {
1071 if (err1->code == 0)
1072 g_string_printf (nmc->return_text, _("Error: 'show active': %s"), err1->message);
1073 else
1074 g_string_printf (nmc->return_text, _("Error: 'show active': %s; allowed fields: %s"), err1->message, NMC_FIELDS_CON_ACTIVE_ALL);
1075 g_error_free (err1);
1076 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1077 }
1078 return nmc->return_value;
1079 }
1080
1081 static NMActiveConnection *
1082 get_default_active_connection (NmCli *nmc, NMDevice **device)
1083 {
1084 NMActiveConnection *default_ac = NULL;
1085 NMDevice *non_default_device = NULL;
1086 NMActiveConnection *non_default_ac = NULL;
1087 const GPtrArray *connections;
1088 int i;
1089
1090 g_return_val_if_fail (nmc != NULL, NULL);
1091 g_return_val_if_fail (device != NULL, NULL);
1092 g_return_val_if_fail (*device == NULL, NULL);
1093
1094 connections = nm_client_get_active_connections (nmc->client);
1095 for (i = 0; connections && (i < connections->len); i++) {
1096 NMActiveConnection *candidate = g_ptr_array_index (connections, i);
1097 const GPtrArray *devices;
1098
1099 devices = nm_active_connection_get_devices (candidate);
1100 if (!devices || !devices->len)
1101 continue;
1102
1103 if (nm_active_connection_get_default (candidate)) {
1104 if (!default_ac) {
1105 *device = g_ptr_array_index (devices, 0);
1106 default_ac = candidate;
1107 }
1108 } else {
1109 if (!non_default_ac) {
1110 non_default_device = g_ptr_array_index (devices, 0);
1111 non_default_ac = candidate;
1112 }
1113 }
1114 }
1115
1116 /* Prefer the default connection if one exists, otherwise return the first
1117 * non-default connection.
1118 */
1119 if (!default_ac && non_default_ac) {
1120 default_ac = non_default_ac;
1121 *device = non_default_device;
1122 }
1123 return default_ac;
1124 }
1125
1126 /* Find a device to activate the connection on.
1127 * IN: connection: connection to activate
1128 * iface: device interface name to use (optional)
1129 * ap: access point to use (optional; valid just for 802-11-wireless)
1130 * nsp: Network Service Provider to use (option; valid only for wimax)
1131 * OUT: device: found device
1132 * spec_object: specific_object path of NMAccessPoint
1133 * RETURNS: TRUE when a device is found, FALSE otherwise.
1134 */
1135 static gboolean
1136 find_device_for_connection (NmCli *nmc,
1137 NMConnection *connection,
1138 const char *iface,
1139 const char *ap,
1140 const char *nsp,
1141 NMDevice **device,
1142 const char **spec_object,
1143 GError **error)
1144 {
1145 NMSettingConnection *s_con;
1146 const char *con_type;
1147 int i, j;
1148
1149 g_return_val_if_fail (nmc != NULL, FALSE);
1150 g_return_val_if_fail (device != NULL && *device == NULL, FALSE);
1151 g_return_val_if_fail (spec_object != NULL && *spec_object == NULL, FALSE);
1152 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1153
1154 s_con = nm_connection_get_setting_connection (connection);
1155 g_assert (s_con);
1156 con_type = nm_setting_connection_get_connection_type (s_con);
1157
1158 if (strcmp (con_type, NM_SETTING_VPN_SETTING_NAME) == 0) {
1159 /* VPN connections */
1160 NMActiveConnection *active = NULL;
1161 if (iface) {
1162 *device = nm_client_get_device_by_iface (nmc->client, iface);
1163 if (*device)
1164 active = nm_device_get_active_connection (*device);
1165
1166 if (!active) {
1167 g_set_error (error, NMCLI_ERROR, 0, _("no active connection on device '%s'"), iface);
1168 return FALSE;
1169 }
1170 *spec_object = nm_object_get_path (NM_OBJECT (active));
1171 return TRUE;
1172 } else {
1173 active = get_default_active_connection (nmc, device);
1174 if (!active) {
1175 g_set_error_literal (error, NMCLI_ERROR, 0, _("no active connection or device"));
1176 return FALSE;
1177 }
1178 *spec_object = nm_object_get_path (NM_OBJECT (active));
1179 return TRUE;
1180 }
1181 } else {
1182 /* Other connections */
1183 NMDevice *found_device = NULL;
1184 const GPtrArray *devices = nm_client_get_devices (nmc->client);
1185
1186 for (i = 0; devices && (i < devices->len) && !found_device; i++) {
1187 NMDevice *dev = g_ptr_array_index (devices, i);
1188
1189 if (iface) {
1190 const char *dev_iface = nm_device_get_iface (dev);
1191 if ( !g_strcmp0 (dev_iface, iface)
1192 && nm_device_connection_compatible (dev, connection, NULL)) {
1193 found_device = dev;
1194 }
1195 } else {
1196 if (nm_device_connection_compatible (dev, connection, NULL)) {
1197 found_device = dev;
1198 }
1199 }
1200
1201 if (found_device && ap && !strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME) && NM_IS_DEVICE_WIFI (dev)) {
1202 char *bssid_up = g_ascii_strup (ap, -1);
1203 const GPtrArray *aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev));
1204 found_device = NULL; /* Mark as not found; set to the device again later, only if AP matches */
1205
1206 for (j = 0; aps && (j < aps->len); j++) {
1207 NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j);
1208 const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap);
1209
1210 if (!strcmp (bssid_up, candidate_bssid)) {
1211 found_device = dev;
1212 *spec_object = nm_object_get_path (NM_OBJECT (candidate_ap));
1213 break;
1214 }
1215 }
1216 g_free (bssid_up);
1217 }
1218
1219 #if WITH_WIMAX
1220 if ( found_device
1221 && nsp
1222 && !strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)
1223 && NM_IS_DEVICE_WIMAX (dev)) {
1224 const GPtrArray *nsps = nm_device_wimax_get_nsps (NM_DEVICE_WIMAX (dev));
1225 found_device = NULL; /* Mark as not found; set to the device again later, only if NSP matches */
1226
1227 for (j = 0; nsps && (j < nsps->len); j++) {
1228 NMWimaxNsp *candidate_nsp = g_ptr_array_index (nsps, j);
1229 const char *candidate_name = nm_wimax_nsp_get_name (candidate_nsp);
1230
1231 if (!strcmp (nsp, candidate_name)) {
1232 found_device = dev;
1233 *spec_object = nm_object_get_path (NM_OBJECT (candidate_nsp));
1234 break;
1235 }
1236 }
1237 }
1238 #endif
1239 }
1240
1241 if (found_device) {
1242 *device = found_device;
1243 return TRUE;
1244 } else {
1245 if (iface)
1246 g_set_error (error, NMCLI_ERROR, 0, _("device '%s' not compatible with connection '%s'"),
1247 iface, nm_setting_connection_get_id (s_con));
1248 else
1249 g_set_error (error, NMCLI_ERROR, 0, _("no device found for connection '%s'"),
1250 nm_setting_connection_get_id (s_con));
1251 return FALSE;
1252 }
1253 }
1254 }
1255
1256 static const char *
1257 vpn_connection_state_reason_to_string (NMVPNConnectionStateReason reason)
1258 {
1259 switch (reason) {
1260 case NM_VPN_CONNECTION_STATE_REASON_UNKNOWN:
1261 return _("unknown reason");
1262 case NM_VPN_CONNECTION_STATE_REASON_NONE:
1263 return _("none");
1264 case NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED:
1265 return _("the user was disconnected");
1266 case NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED:
1267 return _("the base network connection was interrupted");
1268 case NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED:
1269 return _("the VPN service stopped unexpectedly");
1270 case NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID:
1271 return _("the VPN service returned invalid configuration");
1272 case NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT:
1273 return _("the connection attempt timed out");
1274 case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT:
1275 return _("the VPN service did not start in time");
1276 case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED:
1277 return _("the VPN service failed to start");
1278 case NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS:
1279 return _("no valid VPN secrets");
1280 case NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED:
1281 return _("invalid VPN secrets");
1282 case NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED:
1283 return _("the connection was removed");
1284 default:
1285 return _("unknown");
1286 }
1287 }
1288
1289 static void
1290 active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpointer user_data)
1291 {
1292 NmCli *nmc = (NmCli *) user_data;
1293 NMActiveConnectionState state;
1294
1295 state = nm_active_connection_get_state (active);
1296
1297 if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
1298 if (nmc->print_output == NMC_PRINT_PRETTY)
1299 nmc_terminal_erase_line ();
1300 printf (_("Connection successfully activated (D-Bus active path: %s)\n"),
1301 nm_object_get_path (NM_OBJECT (active)));
1302 quit ();
1303 } else if ( state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED
1304 || state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) {
1305 g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
1306 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
1307 quit ();
1308 }
1309 }
1310
1311 static void
1312 vpn_connection_state_cb (NMVPNConnection *vpn,
1313 NMVPNConnectionState state,
1314 NMVPNConnectionStateReason reason,
1315 gpointer user_data)
1316 {
1317 NmCli *nmc = (NmCli *) user_data;
1318
1319 switch (state) {
1320 case NM_VPN_CONNECTION_STATE_PREPARE:
1321 case NM_VPN_CONNECTION_STATE_NEED_AUTH:
1322 case NM_VPN_CONNECTION_STATE_CONNECT:
1323 case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
1324 /* no operation */
1325 break;
1326
1327 case NM_VPN_CONNECTION_STATE_ACTIVATED:
1328 if (nmc->print_output == NMC_PRINT_PRETTY)
1329 nmc_terminal_erase_line ();
1330 printf (_("VPN connection successfully activated (D-Bus active path: %s)\n"),
1331 nm_object_get_path (NM_OBJECT (vpn)));
1332 quit ();
1333 break;
1334
1335 case NM_VPN_CONNECTION_STATE_FAILED:
1336 case NM_VPN_CONNECTION_STATE_DISCONNECTED:
1337 g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."),
1338 vpn_connection_state_reason_to_string (reason));
1339 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
1340 quit ();
1341 break;
1342
1343 default:
1344 break;
1345 }
1346 }
1347
1348 static gboolean
1349 timeout_cb (gpointer user_data)
1350 {
1351 /* Time expired -> exit nmcli */
1352
1353 NmCli *nmc = (NmCli *) user_data;
1354
1355 g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout);
1356 nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED;
1357 quit ();
1358 return FALSE;
1359 }
1360
1361 static gboolean
1362 progress_cb (gpointer user_data)
1363 {
1364 const char *str = (const char *) user_data;
1365
1366 nmc_terminal_show_progress (str);
1367
1368 return TRUE;
1369 }
1370
1371 static gboolean
1372 progress_device_cb (gpointer user_data)
1373 {
1374 NMDevice *device = (NMDevice *) user_data;
1375
1376 nmc_terminal_show_progress (device ? nmc_device_state_to_string (nm_device_get_state (device)) : "");
1377
1378 return TRUE;
1379 }
1380
1381 static gboolean
1382 progress_vpn_cb (gpointer user_data)
1383 {
1384 NMVPNConnection *vpn = (NMVPNConnection *) user_data;
1385 const char *str;
1386
1387 str = NM_IS_VPN_CONNECTION (vpn) ?
1388 vpn_connection_state_to_string (nm_vpn_connection_get_vpn_state (vpn)) :
1389 "";
1390
1391 nmc_terminal_show_progress (str);
1392
1393 return TRUE;
1394 }
1395
1396 typedef struct {
1397 NmCli *nmc;
1398 NMDevice *device;
1399 } ActivateConnectionInfo;
1400
1401 static gboolean
1402 master_iface_slaves_check (gpointer user_data)
1403 {
1404 ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
1405 NmCli *nmc = info->nmc;
1406 NMDevice *device = info->device;
1407 const GPtrArray *slaves = NULL;
1408
1409 if (NM_IS_DEVICE_BOND (device))
1410 slaves = nm_device_bond_get_slaves (NM_DEVICE_BOND (device));
1411 else if (NM_IS_DEVICE_TEAM (device))
1412 slaves = nm_device_team_get_slaves (NM_DEVICE_TEAM (device));
1413 else if (NM_IS_DEVICE_BRIDGE (device))
1414 slaves = nm_device_bridge_get_slaves (NM_DEVICE_BRIDGE (device));
1415 else
1416 g_warning ("%s: should not be reached.", __func__);
1417
1418 if (!slaves) {
1419 g_string_printf (nmc->return_text,
1420 _("Error: Device '%s' is waiting for slaves before proceeding with activation."),
1421 nm_device_get_iface (device));
1422 nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED;
1423 quit ();
1424 }
1425
1426 g_free (info);
1427 return FALSE;
1428 }
1429
1430 static void
1431 activate_connection_cb (NMClient *client, NMActiveConnection *active, GError *error, gpointer user_data)
1432 {
1433 ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
1434 NmCli *nmc = info->nmc;
1435 NMDevice *device = info->device;
1436 NMActiveConnectionState state;
1437 const GPtrArray *ac_devs;
1438
1439 if (error) {
1440 g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"), error->message);
1441 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
1442 quit ();
1443 } else {
1444 state = nm_active_connection_get_state (active);
1445 if (!device) {
1446 /* device could be NULL for virtual devices. Fill it here. */
1447 ac_devs = nm_active_connection_get_devices (active);
1448 info->device = device = ac_devs && ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
1449 }
1450
1451 if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
1452 /* User doesn't want to wait or already activated */
1453 if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
1454 if (nmc->print_output == NMC_PRINT_PRETTY)
1455 nmc_terminal_erase_line ();
1456 printf (_("Connection successfully activated (D-Bus active path: %s)\n"),
1457 nm_object_get_path (NM_OBJECT (active)));
1458 }
1459 quit ();
1460 } else {
1461 if (NM_IS_VPN_CONNECTION (active)) {
1462 /* Monitor VPN state */
1463 g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc);
1464
1465 /* Start progress indication showing VPN states */
1466 if (nmc->print_output == NMC_PRINT_PRETTY) {
1467 if (progress_id)
1468 g_source_remove (progress_id);
1469 progress_id = g_timeout_add (120, progress_vpn_cb, NM_VPN_CONNECTION (active));
1470 }
1471 } else {
1472 g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), nmc);
1473
1474 /* Start progress indication showing device states */
1475 if (nmc->print_output == NMC_PRINT_PRETTY) {
1476 if (progress_id)
1477 g_source_remove (progress_id);
1478 progress_id = g_timeout_add (120, progress_device_cb, device);
1479 }
1480 }
1481
1482 /* Start timer not to loop forever when signals are not emitted */
1483 g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc);
1484
1485 /* Check for bond or team or bridge slaves */
1486 if ( NM_IS_DEVICE_BOND (device)
1487 || NM_IS_DEVICE_TEAM (device)
1488 || NM_IS_DEVICE_BRIDGE (device)) {
1489 g_timeout_add_seconds (SLAVES_UP_TIMEOUT, master_iface_slaves_check, info);
1490 return; /* info will be freed in master_iface_slaves_check () */
1491 }
1492 }
1493 }
1494 g_free (info);
1495 }
1496
1497 static NMCResultCode
1498 do_connection_up (NmCli *nmc, int argc, char **argv)
1499 {
1500 ActivateConnectionInfo *info;
1501 NMDevice *device = NULL;
1502 const char *spec_object = NULL;
1503 gboolean device_found;
1504 NMConnection *connection = NULL;
1505 const char *ifname = NULL;
1506 const char *ap = NULL;
1507 const char *nsp = NULL;
1508 GError *error = NULL;
1509 gboolean is_virtual = FALSE;
1510 const char *selector = NULL;
1511 const char *name;
1512 char *line = NULL;
1513
1514 /*
1515 * Set default timeout for connection activation.
1516 * Activation can take quite a long time, use 90 seconds.
1517 */
1518 if (nmc->timeout == -1)
1519 nmc->timeout = 90;
1520
1521 if (argc == 0) {
1522 if (nmc->ask) {
1523 line = nmc_get_user_input (_("Connection (name, UUID, or path): "));
1524 name = line ? line : "";
1525 // TODO: enhancement: when just Enter is pressed (line is NULL), list
1526 // available connections so that the user can select one
1527 } else {
1528 g_string_printf (nmc->return_text, _("Error: No connection specified."));
1529 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1530 goto error;
1531 }
1532 } else {
1533 if ( strcmp (*argv, "id") == 0
1534 || strcmp (*argv, "uuid") == 0
1535 || strcmp (*argv, "path") == 0) {
1536
1537 selector = *argv;
1538 if (next_arg (&argc, &argv) != 0) {
1539 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
1540 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1541 goto error;
1542 }
1543 name = *argv;
1544 }
1545 name = *argv;
1546 }
1547
1548 connection = find_connection (nmc->system_connections, selector, name);
1549
1550 if (!connection) {
1551 g_string_printf (nmc->return_text, _("Error: Unknown connection: %s."), name);
1552 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
1553 goto error;
1554 }
1555 next_arg (&argc, &argv);
1556
1557 while (argc > 0) {
1558 if (strcmp (*argv, "ifname") == 0) {
1559 if (next_arg (&argc, &argv) != 0) {
1560 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
1561 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1562 goto error;
1563 }
1564
1565 ifname = *argv;
1566 }
1567 else if (strcmp (*argv, "ap") == 0) {
1568 if (next_arg (&argc, &argv) != 0) {
1569 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
1570 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1571 goto error;
1572 }
1573
1574 ap = *argv;
1575 }
1576 #if WITH_WIMAX
1577 else if (strcmp (*argv, "nsp") == 0) {
1578 if (next_arg (&argc, &argv) != 0) {
1579 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
1580 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1581 goto error;
1582 }
1583
1584 nsp = *argv;
1585 }
1586 #endif
1587 else {
1588 fprintf (stderr, _("Unknown parameter: %s\n"), *argv);
1589 }
1590
1591 argc--;
1592 argv++;
1593 }
1594
1595 /* create NMClient */
1596 nmc->get_client (nmc);
1597
1598 if (!nm_client_get_manager_running (nmc->client)) {
1599 g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
1600 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
1601 goto error;
1602 }
1603
1604 if (nm_connection_get_virtual_iface_name (connection))
1605 is_virtual = TRUE;
1606
1607 device_found = find_device_for_connection (nmc, connection, ifname, ap, nsp, &device, &spec_object, &error);
1608 /* Virtual connection may not have their interfaces created yet */
1609 if (!device_found && !is_virtual) {
1610 if (error)
1611 g_string_printf (nmc->return_text, _("Error: No suitable device found: %s."), error->message);
1612 else
1613 g_string_printf (nmc->return_text, _("Error: No suitable device found."));
1614 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
1615 g_clear_error (&error);
1616 goto error;
1617 }
1618
1619 /* Use nowait_flag instead of should_wait because exiting has to be postponed till
1620 * active_connection_state_cb() is called. That gives NM time to check our permissions
1621 * and we can follow activation progress.
1622 */
1623 nmc->nowait_flag = (nmc->timeout == 0);
1624 nmc->should_wait = TRUE;
1625
1626 info = g_malloc0 (sizeof (ActivateConnectionInfo));
1627 info->nmc = nmc;
1628 info->device = device;
1629
1630 nm_client_activate_connection (nmc->client,
1631 connection,
1632 device,
1633 spec_object,
1634 activate_connection_cb,
1635 info);
1636
1637 /* Start progress indication */
1638 if (nmc->print_output == NMC_PRINT_PRETTY)
1639 progress_id = g_timeout_add (120, progress_cb, _("preparing"));
1640
1641 g_free (line);
1642 return nmc->return_value;
1643 error:
1644 nmc->should_wait = FALSE;
1645 g_free (line);
1646 return nmc->return_value;
1647 }
1648
1649 static NMCResultCode
1650 do_connection_down (NmCli *nmc, int argc, char **argv)
1651 {
1652 NMActiveConnection *active;
1653 const GPtrArray *active_cons;
1654 char *line = NULL;
1655 char **arg_arr = NULL;
1656 char **arg_ptr = argv;
1657 int arg_num = argc;
1658
1659 if (argc == 0) {
1660 if (nmc->ask) {
1661 line = nmc_get_user_input (_("Connection (name, UUID, or path): "));
1662 nmc_string_to_arg_array (line, "", &arg_arr, &arg_num);
1663 arg_ptr = arg_arr;
1664 }
1665 if (arg_num == 0) {
1666 g_string_printf (nmc->return_text, _("Error: No connection specified."));
1667 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1668 goto error;
1669 }
1670 }
1671
1672 /* create NMClient */
1673 nmc->get_client (nmc);
1674
1675 if (!nm_client_get_manager_running (nmc->client)) {
1676 g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
1677 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
1678 goto error;
1679 }
1680
1681 /* Get active connections */
1682 active_cons = nm_client_get_active_connections (nmc->client);
1683 while (arg_num > 0) {
1684 const char *selector = NULL;
1685
1686 if ( strcmp (*arg_ptr, "id") == 0
1687 || strcmp (*arg_ptr, "uuid") == 0
1688 || strcmp (*arg_ptr, "path") == 0
1689 || strcmp (*arg_ptr, "apath") == 0) {
1690
1691 selector = *arg_ptr;
1692 if (next_arg (&arg_num, &arg_ptr) != 0) {
1693 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
1694 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1695 goto error;
1696 }
1697 }
1698
1699 active = find_active_connection (active_cons, nmc->system_connections, selector, *arg_ptr);
1700 if (active) {
1701 nm_client_deactivate_connection (nmc->client, active);
1702 } else {
1703 g_string_printf (nmc->return_text, _("Error: '%s' is not an active connection."), *arg_ptr);
1704 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
1705 goto error;
1706 }
1707
1708 next_arg (&arg_num, &arg_ptr);
1709 }
1710
1711 // FIXME: do something better then sleep()
1712 /* Don't quit immediatelly and give NM time to check our permissions */
1713 sleep (1);
1714
1715 error:
1716 nmc->should_wait = FALSE;
1717 g_strfreev (arg_arr);
1718 return nmc->return_value;
1719 }
1720
1721 /*----------------------------------------------------------------------------*/
1722
1723 typedef struct NameItem {
1724 const char *name;
1725 const char *alias;
1726 const struct NameItem *settings;
1727 gboolean mandatory;
1728 } NameItem;
1729
1730 static const NameItem nmc_ethernet_settings [] = {
1731 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1732 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE },
1733 { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE },
1734 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1735 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1736 { NULL, NULL, NULL, FALSE }
1737 };
1738
1739 static const NameItem nmc_infiniband_settings [] = {
1740 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1741 { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, NULL, TRUE },
1742 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1743 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1744 { NULL, NULL, NULL, FALSE }
1745 };
1746
1747 static const NameItem nmc_wifi_settings [] = {
1748 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1749 { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", NULL, TRUE },
1750 { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-sec", NULL, FALSE },
1751 { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE },
1752 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1753 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1754 { NULL, NULL, NULL, FALSE }
1755 };
1756
1757 static const NameItem nmc_wimax_settings [] = {
1758 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1759 { NM_SETTING_WIMAX_SETTING_NAME, NULL, NULL, TRUE },
1760 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1761 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1762 { NULL, NULL, NULL, FALSE }
1763 };
1764
1765 static const NameItem nmc_gsm_settings [] = {
1766 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1767 { NM_SETTING_GSM_SETTING_NAME, NULL, NULL, TRUE },
1768 { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE },
1769 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1770 { NULL, NULL, NULL, FALSE }
1771 };
1772
1773 static const NameItem nmc_cdma_settings [] = {
1774 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1775 { NM_SETTING_CDMA_SETTING_NAME, NULL, NULL, TRUE },
1776 { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE },
1777 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1778 { NULL, NULL, NULL, FALSE }
1779 };
1780
1781 static const NameItem nmc_mobile_settings [] = {
1782 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1783 { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE },
1784 { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE },
1785 { NM_SETTING_GSM_SETTING_NAME, NULL, NULL, TRUE },
1786 { NM_SETTING_CDMA_SETTING_NAME, NULL, NULL, TRUE },
1787 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1788 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1789 { NULL, NULL, NULL, FALSE }
1790 };
1791
1792 static const NameItem nmc_bluetooth_settings [] = {
1793 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1794 { NM_SETTING_BLUETOOTH_SETTING_NAME, NULL, NULL, TRUE },
1795 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1796 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1797 { NULL, NULL, NULL, FALSE }
1798 };
1799
1800 static const NameItem nmc_adsl_settings [] = {
1801 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1802 { NM_SETTING_ADSL_SETTING_NAME, NULL, NULL, TRUE },
1803 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1804 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1805 { NULL, NULL, NULL, FALSE }
1806 };
1807
1808 static const NameItem nmc_ppoe_settings [] = {
1809 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1810 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
1811 { NM_SETTING_PPPOE_SETTING_NAME, NULL, NULL, TRUE },
1812 { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE },
1813 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1814 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1815 { NULL, NULL, NULL, FALSE }
1816 };
1817
1818 static const NameItem nmc_olpc_mesh_settings [] = {
1819 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1820 { NM_SETTING_OLPC_MESH_SETTING_NAME, "olpc-mesh", NULL, TRUE },
1821 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1822 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1823 { NULL, NULL, NULL, FALSE }
1824 };
1825
1826 static const NameItem nmc_vpn_settings [] = {
1827 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1828 { NM_SETTING_VPN_SETTING_NAME, NULL, NULL, TRUE },
1829 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1830 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1831 { NULL, NULL, NULL, FALSE }
1832 };
1833
1834 static const NameItem nmc_vlan_settings [] = {
1835 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1836 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
1837 { NM_SETTING_VLAN_SETTING_NAME, NULL, NULL, TRUE },
1838 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1839 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1840 { NULL, NULL, NULL, FALSE }
1841 };
1842
1843 static const NameItem nmc_bond_settings [] = {
1844 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1845 { NM_SETTING_BOND_SETTING_NAME, NULL, NULL, TRUE },
1846 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
1847 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1848 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1849 { NULL, NULL, NULL, FALSE }
1850 };
1851
1852 static const NameItem nmc_team_settings [] = {
1853 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1854 { NM_SETTING_TEAM_SETTING_NAME, NULL, NULL, TRUE },
1855 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
1856 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1857 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1858 { NULL, NULL, NULL, FALSE }
1859 };
1860
1861 static const NameItem nmc_bridge_settings [] = {
1862 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1863 { NM_SETTING_BRIDGE_SETTING_NAME, NULL, NULL, TRUE },
1864 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
1865 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1866 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
1867 { NULL, NULL, NULL, FALSE }
1868 };
1869
1870 static const NameItem nmc_bond_slave_settings [] = {
1871 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1872 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE },
1873 { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE },
1874 { NULL, NULL, NULL, FALSE }
1875 };
1876
1877 static const NameItem nmc_team_slave_settings [] = {
1878 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1879 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE },
1880 { NM_SETTING_TEAM_PORT_SETTING_NAME, NULL, NULL, TRUE },
1881 { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE },
1882 { NULL, NULL, NULL, FALSE }
1883 };
1884
1885 static const NameItem nmc_bridge_slave_settings [] = {
1886 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
1887 { NM_SETTING_BRIDGE_PORT_SETTING_NAME, NULL, NULL, TRUE },
1888 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE },
1889 { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE },
1890 { NULL, NULL, NULL, FALSE }
1891 };
1892
1893
1894 /* Available connection types */
1895 static const NameItem nmc_valid_connection_types[] = {
1896 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", nmc_ethernet_settings },
1897 { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", nmc_wifi_settings },
1898 { NM_SETTING_WIMAX_SETTING_NAME, NULL, nmc_wimax_settings },
1899 { NM_SETTING_GSM_SETTING_NAME, NULL, nmc_gsm_settings },
1900 { NM_SETTING_CDMA_SETTING_NAME, NULL, nmc_cdma_settings },
1901 { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, nmc_infiniband_settings },
1902 { NM_SETTING_ADSL_SETTING_NAME, NULL, nmc_adsl_settings },
1903 { NM_SETTING_BLUETOOTH_SETTING_NAME, NULL, nmc_bluetooth_settings },
1904 { NM_SETTING_VPN_SETTING_NAME, NULL, nmc_vpn_settings },
1905 { NM_SETTING_OLPC_MESH_SETTING_NAME, "olpc-mesh", nmc_olpc_mesh_settings },
1906 { NM_SETTING_VLAN_SETTING_NAME, NULL, nmc_vlan_settings },
1907 { NM_SETTING_BOND_SETTING_NAME, NULL, nmc_bond_settings },
1908 { NM_SETTING_TEAM_SETTING_NAME, NULL, nmc_team_settings },
1909 { NM_SETTING_BRIDGE_SETTING_NAME, NULL, nmc_bridge_settings },
1910 { "bond-slave", NULL, nmc_bond_slave_settings },
1911 { "team-slave", NULL, nmc_team_slave_settings },
1912 { "bridge-slave", NULL, nmc_bridge_slave_settings },
1913 { NULL, NULL, NULL }
1914 };
1915
1916 /*
1917 * Return an alias for the 'name' if exists, else return the 'name'.
1918 * The returned string must not be freed.
1919 */
1920 static const char *
1921 get_name_alias (const char *name, const NameItem array[])
1922 {
1923 const NameItem *iter = &array[0];
1924
1925 if (!name)
1926 return NULL;
1927
1928 while (iter && iter->name) {
1929 if (!strcmp (name, iter->name)) {
1930 if (iter->alias)
1931 return iter->alias;
1932 else
1933 return iter->name;
1934 }
1935 iter++;
1936 }
1937 return name;
1938 }
1939
1940 /*
1941 * Construct a string with names and aliases from the array formatted as:
1942 * "name (alias), name, name (alias), name, name"
1943 *
1944 * Returns: string; the caller is responsible for freeing it.
1945 */
1946 static char *
1947 get_valid_options_string (const NameItem array[])
1948 {
1949 const NameItem *iter = &array[0];
1950 GString *str;
1951
1952 str = g_string_sized_new (150);
1953 while (iter && iter->name) {
1954 if (str->len)
1955 g_string_append (str, ", ");
1956 if (iter->alias)
1957 g_string_append_printf (str, "%s (%s)", iter->name, iter->alias);
1958 else
1959 g_string_append (str, iter->name);
1960 iter++;
1961 }
1962 return g_string_free (str, FALSE);
1963 }
1964
1965 /*
1966 * Check if 'val' is valid string in either array->name or array->alias.
1967 * It accepts shorter string provided they are not ambiguous.
1968 * 'val' == NULL doesn't hurt.
1969 *
1970 * Returns: pointer to array->name string or NULL on failure.
1971 * The returned string must not be freed.
1972 */
1973 static const char *
1974 check_valid_name (const char *val, const NameItem array[], GError **error)
1975 {
1976 const NameItem *iter;
1977 GPtrArray *tmp_arr;
1978 const char *str;
1979 GError *tmp_err = NULL;
1980
1981 /* Create a temporary array that can be used in nmc_string_is_valid() */
1982 tmp_arr = g_ptr_array_sized_new (30);
1983 iter = &array[0];
1984 while (iter && iter->name) {
1985 g_ptr_array_add (tmp_arr, (gpointer) iter->name);
1986 if (iter->alias)
1987 g_ptr_array_add (tmp_arr, (gpointer) iter->alias);
1988 iter++;
1989 }
1990 g_ptr_array_add (tmp_arr, (gpointer) NULL);
1991
1992 /* Check string validity */
1993 str = nmc_string_is_valid (val, (const char **) tmp_arr->pdata, &tmp_err);
1994 if (!str) {
1995 if (tmp_err->code == 1)
1996 g_propagate_error (error, tmp_err);
1997 else {
1998 /* We want to handle aliases, so construct own error message */
1999 char *err_str = get_valid_options_string (array);
2000 g_set_error (error, 1, 0, _("'%s' not among [%s]"),
2001 val ? val : "", err_str);
2002 g_free (err_str);
2003 g_clear_error (&tmp_err);
2004 }
2005 g_ptr_array_free (tmp_arr, TRUE);
2006 return NULL;
2007 }
2008
2009 /* Return a pointer to the found string in passed 'array' */
2010 iter = &array[0];
2011 while (iter && iter->name) {
2012 if ( (iter->name && g_strcmp0 (iter->name, str) == 0)
2013 || (iter->alias && g_strcmp0 (iter->alias, str) == 0)) {
2014 g_ptr_array_free (tmp_arr, TRUE);
2015 return iter->name;
2016 }
2017 iter++;
2018 }
2019 /* We should not really come here */
2020 g_ptr_array_free (tmp_arr, TRUE);
2021 g_set_error (error, 1, 0, _("Unknown error"));
2022 return NULL;
2023 }
2024
2025 static const NameItem *
2026 get_valid_settings_array (const char *con_type)
2027 {
2028 guint i, num;
2029
2030 if (!con_type)
2031 return NULL;
2032
2033 num = G_N_ELEMENTS (nmc_valid_connection_types);
2034 for (i = 0; i < num; i++) {
2035 if (!strcmp (con_type, nmc_valid_connection_types[i].name))
2036 return nmc_valid_connection_types[i].settings;
2037 }
2038 return NULL;
2039 }
2040
2041 static gboolean
2042 is_setting_mandatory (NMConnection *connection, NMSetting *setting)
2043 {
2044 NMSettingConnection *s_con;
2045 const char *c_type;
2046 const NameItem *item;
2047 const char *name;
2048
2049 s_con = nm_connection_get_setting_connection (connection);
2050 g_assert (s_con);
2051 c_type = nm_setting_connection_get_connection_type (s_con);
2052
2053 name = nm_setting_get_name (setting);
2054
2055 item = get_valid_settings_array (c_type);
2056 while (item && item->name) {
2057 if (!strcmp (name, item->name))
2058 return item->mandatory;
2059 item++;
2060 }
2061 return FALSE;
2062 }
2063
2064 /*----------------------------------------------------------------------------*/
2065
2066 static gboolean
2067 check_and_convert_mac (const char *mac,
2068 GByteArray **mac_array,
2069 int type,
2070 const char *keyword,
2071 GError **error)
2072 {
2073 GByteArray *local_mac_array = NULL;
2074 g_return_val_if_fail (mac_array == NULL || *mac_array == NULL, FALSE);
2075
2076 if (!mac)
2077 return TRUE;
2078
2079 local_mac_array = nm_utils_hwaddr_atoba (mac, type);
2080 if (!local_mac_array) {
2081 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2082 _("Error: '%s': '%s' is not a valid %s MAC address."),
2083 keyword, mac, type == ARPHRD_INFINIBAND ? _("InfiniBand") : _("Ethernet"));
2084 return FALSE;
2085 }
2086
2087 if (mac_array)
2088 *mac_array = local_mac_array;
2089 else
2090 if (local_mac_array)
2091 g_byte_array_free (local_mac_array, TRUE);
2092
2093 return TRUE;
2094 }
2095
2096 static gboolean
2097 check_and_convert_mtu (const char *mtu, guint32 *mtu_int, GError **error)
2098 {
2099 unsigned long local_mtu_int;
2100
2101 if (!mtu)
2102 return TRUE;
2103
2104 if (!nmc_string_to_uint (mtu, TRUE, 0, G_MAXUINT32, &local_mtu_int)) {
2105 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2106 _("Error: 'mtu': '%s' is not a valid MTU."), mtu);
2107 return FALSE;
2108 }
2109 if (mtu_int)
2110 *mtu_int = (guint32) local_mtu_int;
2111 return TRUE;
2112 }
2113
2114 static gboolean
2115 check_infiniband_parent (const char *parent, GError **error)
2116 {
2117 if (!parent)
2118 return TRUE;
2119
2120 if (!nm_utils_iface_valid_name (parent)) {
2121 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2122 _("Error: 'parent': '%s' is not a valid interface name."), parent);
2123 return FALSE;
2124 }
2125 return TRUE;
2126 }
2127
2128
2129 static gboolean
2130 check_infiniband_p_key (const char *p_key, guint32 *p_key_int, GError **error)
2131 {
2132 unsigned long local_p_key_int;
2133 gboolean p_key_valid = FALSE;
2134 if (!p_key)
2135 return TRUE;
2136
2137 if (!strncmp (p_key, "0x", 2))
2138 p_key_valid = nmc_string_to_uint_base (p_key + 2, 16, TRUE, 0, G_MAXUINT16, &local_p_key_int);
2139 else
2140 p_key_valid = nmc_string_to_uint (p_key, TRUE, 0, G_MAXUINT16, &local_p_key_int);
2141 if (!p_key_valid) {
2142 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2143 _("Error: 'p-key': '%s' is not a valid InfiniBand P_KEY."), p_key);
2144 return FALSE;
2145 }
2146 if (p_key_int)
2147 *p_key_int = (guint32) local_p_key_int;
2148 return TRUE;
2149 }
2150
2151 static gboolean
2152 check_infiniband_mode (const char *mode, GError **error)
2153 {
2154 if (!mode)
2155 return TRUE;
2156
2157 if (strcmp (mode, "datagram") && strcmp (mode, "connected")) {
2158 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2159 _("Error: 'mode': '%s' is not a valid InfiniBand transport mode [datagram, connected]."), mode);
2160 return FALSE;
2161 }
2162 return TRUE;
2163 }
2164
2165 static gboolean
2166 check_and_convert_vlan_flags (const char *flags, guint32 *flags_int, GError **error)
2167 {
2168 unsigned long local_flags_int;
2169
2170 if (!flags)
2171 return TRUE;
2172
2173 if (!nmc_string_to_uint (flags, TRUE, 0, 7, &local_flags_int)) {
2174 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2175 _("Error: 'flags': '%s' is not valid; use <0-7>."), flags);
2176 return FALSE;
2177 }
2178 if (flags_int)
2179 *flags_int = (guint32) local_flags_int;
2180 return TRUE;
2181 }
2182
2183 static gboolean
2184 check_and_convert_vlan_prio_maps (const char *prio_map,
2185 NMVlanPriorityMap type,
2186 char ***prio_map_arr,
2187 GError **error)
2188 {
2189 char **local_prio_map_arr;
2190 GError *local_err = NULL;
2191
2192 if (!prio_map)
2193 return TRUE;
2194
2195 if (!(local_prio_map_arr = nmc_vlan_parse_priority_maps (prio_map, type, &local_err))) {
2196 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2197 _("Error: '%s': '%s' is not valid; %s "),
2198 type == NM_VLAN_INGRESS_MAP ? "ingress" : "egress",
2199 prio_map, local_err->message);
2200 return FALSE;
2201 }
2202
2203 if (prio_map_arr)
2204 *prio_map_arr = local_prio_map_arr;
2205 return TRUE;
2206 }
2207
2208 static gboolean
2209 add_ip4_address_to_connection (NMIP4Address *ip4addr, NMConnection *connection)
2210 {
2211 NMSettingIP4Config *s_ip4;
2212 gboolean ret;
2213
2214 if (!ip4addr)
2215 return TRUE;
2216
2217 s_ip4 = nm_connection_get_setting_ip4_config (connection);
2218 if (!s_ip4) {
2219 s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
2220 nm_connection_add_setting (connection, NM_SETTING (s_ip4));
2221 g_object_set (s_ip4,
2222 NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
2223 NULL);
2224 }
2225 ret = nm_setting_ip4_config_add_address (s_ip4, ip4addr);
2226 nm_ip4_address_unref (ip4addr);
2227
2228 return ret;
2229 }
2230
2231 static gboolean
2232 add_ip6_address_to_connection (NMIP6Address *ip6addr, NMConnection *connection)
2233 {
2234 NMSettingIP6Config *s_ip6;
2235 gboolean ret;
2236
2237 if (!ip6addr)
2238 return TRUE;
2239
2240 s_ip6 = nm_connection_get_setting_ip6_config (connection);
2241 if (!s_ip6) {
2242 s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
2243 nm_connection_add_setting (connection, NM_SETTING (s_ip6));
2244 g_object_set (s_ip6,
2245 NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
2246 NULL);
2247 }
2248 ret = nm_setting_ip6_config_add_address (s_ip6, ip6addr);
2249 nm_ip6_address_unref (ip6addr);
2250
2251 return ret;
2252 }
2253
2254 static char *
2255 unique_master_iface_ifname (GSList *list,
2256 const char *type,
2257 const char *ifname_property,
2258 const char *try_name)
2259 {
2260 NMConnection *connection;
2261 NMSetting *setting;
2262 char *new_name;
2263 unsigned int num = 1;
2264 GSList *iterator = list;
2265 char *ifname_val = NULL;
2266
2267 new_name = g_strdup (try_name);
2268 while (iterator) {
2269 connection = NM_CONNECTION (iterator->data);
2270 setting = nm_connection_get_setting_by_name (connection, type);
2271 if (!setting) {
2272 iterator = g_slist_next (iterator);
2273 continue;
2274 }
2275
2276 g_object_get (setting, ifname_property, &ifname_val, NULL);
2277 if (g_strcmp0 (new_name, ifname_val) == 0) {
2278 g_free (new_name);
2279 new_name = g_strdup_printf ("%s%d", try_name, num++);
2280 iterator = list;
2281 } else
2282 iterator = g_slist_next (iterator);
2283 g_free (ifname_val);
2284 }
2285 return new_name;
2286 }
2287
2288 static gboolean
2289 bridge_prop_string_to_uint (const char *str,
2290 const char *nmc_arg,
2291 GType bridge_type,
2292 const char *propname,
2293 unsigned long *out_val,
2294 GError **error)
2295 {
2296 GParamSpecUInt *pspec;
2297
2298 pspec = (GParamSpecUInt *) g_object_class_find_property (g_type_class_peek (bridge_type),
2299 propname);
2300 g_assert (G_IS_PARAM_SPEC_UINT (pspec));
2301
2302 if (!nmc_string_to_uint (str, TRUE, pspec->minimum, pspec->maximum, out_val)) {
2303 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2304 _("Error: '%s': '%s' is not valid; use <%u-%u>."),
2305 nmc_arg, str, pspec->minimum, pspec->maximum);
2306 return FALSE;
2307 }
2308 return TRUE;
2309 }
2310
2311 static void
2312 do_questionnaire_ethernet (gboolean ethernet, char **mtu, char **mac, char **cloned_mac)
2313 {
2314 char *answer;
2315 gboolean answer_bool;
2316 gboolean once_more;
2317 GError *error = NULL;
2318 const char *type = ethernet ? _("ethernet") : _("Wi-Fi");
2319
2320 /* Ask for optional arguments */
2321 printf (_("There are 3 optional arguments for '%s' connection type.\n"), type);;
2322 answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2323 if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2324 g_free (answer);
2325 return;
2326 }
2327
2328 if (!*mtu) {
2329 do {
2330 *mtu = nmc_get_user_input (_("MTU [auto]: "));
2331 once_more = !check_and_convert_mtu (*mtu, NULL, &error);
2332 if (once_more) {
2333 printf ("%s\n", error->message);
2334 g_clear_error (&error);
2335 g_free (*mtu);
2336 }
2337 } while (once_more);
2338 }
2339 if (!*mac) {
2340 do {
2341 *mac = nmc_get_user_input (_("MAC [none]: "));
2342 once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_ETHER, "mac", &error);
2343 if (once_more) {
2344 printf ("%s\n", error->message);
2345 g_clear_error (&error);
2346 g_free (*mac);
2347 }
2348 } while (once_more);
2349 }
2350 if (!*cloned_mac) {
2351 do {
2352 *cloned_mac = nmc_get_user_input (_("Cloned MAC [none]: "));
2353 once_more = !check_and_convert_mac (*cloned_mac, NULL, ARPHRD_ETHER, "cloned-mac", &error);
2354 if (once_more) {
2355 printf ("%s\n", error->message);
2356 g_clear_error (&error);
2357 g_free (*cloned_mac);
2358 }
2359 } while (once_more);
2360 }
2361
2362 g_free (answer);
2363 return;
2364 }
2365
2366 static void
2367 do_questionnaire_infiniband (char **mtu, char **mac, char **mode, char **parent, char **p_key)
2368 {
2369 char *answer;
2370 gboolean answer_bool;
2371 gboolean once_more;
2372 GError *error = NULL;
2373
2374 /* Ask for optional arguments */
2375 printf (_("There are 5 optional arguments for 'InfiniBand' connection type.\n"));
2376 answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2377 if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2378 g_free (answer);
2379 return;
2380 }
2381
2382 if (!*mtu) {
2383 do {
2384 *mtu = nmc_get_user_input (_("MTU [auto]: "));
2385 once_more = !check_and_convert_mtu (*mtu, NULL, &error);
2386 if (once_more) {
2387 printf ("%s\n", error->message);
2388 g_clear_error (&error);
2389 g_free (*mtu);
2390 }
2391 } while (once_more);
2392 }
2393 if (!*mac) {
2394 do {
2395 *mac = nmc_get_user_input (_("MAC [none]: "));
2396 once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_INFINIBAND, "mac", &error);
2397 if (once_more) {
2398 printf ("%s\n", error->message);
2399 g_clear_error (&error);
2400 g_free (*mac);
2401 }
2402 } while (once_more);
2403 }
2404 if (!*mode) {
2405 do {
2406 *mode = nmc_get_user_input (_("Transport mode (datagram or connected) [datagram]: "));
2407 if (!*mode)
2408 *mode = g_strdup ("datagram");
2409 once_more = !check_infiniband_mode (*mode, &error);
2410 if (once_more) {
2411 printf ("%s\n", error->message);
2412 g_clear_error (&error);
2413 g_free (*mode);
2414 }
2415 } while (once_more);
2416 }
2417 if (!*parent) {
2418 do {
2419 *parent = nmc_get_user_input (_("Parent interface [none]: "));
2420 once_more = !check_infiniband_parent (*parent, &error);
2421 if (once_more) {
2422 printf ("%s\n", error->message);
2423 g_clear_error (&error);
2424 g_free (*parent);
2425 }
2426 } while (once_more);
2427 }
2428 if (!*p_key) {
2429 do {
2430 *p_key = nmc_get_user_input (_("P_KEY [none]: "));
2431 once_more = !check_infiniband_p_key (*p_key, NULL, &error);
2432 if (once_more) {
2433 printf ("%s\n", error->message);
2434 g_clear_error (&error);
2435 g_free (*p_key);
2436 }
2437 /* If parent is specified, so has to be P_KEY */
2438 if (!once_more && *parent && !*p_key) {
2439 once_more = TRUE;
2440 printf (_("Error: 'p-key' is mandatory when 'parent' is specified.\n"));
2441 }
2442 } while (once_more);
2443 }
2444
2445 g_free (answer);
2446 return;
2447 }
2448
2449 static void
2450 do_questionnaire_wifi (char **mtu, char **mac, char **cloned_mac)
2451 {
2452 /* At present, the optional Wi-Fi arguments are the same as for ethernet. */
2453 return do_questionnaire_ethernet (FALSE, mtu, mac, cloned_mac);
2454 }
2455
2456 static void
2457 do_questionnaire_wimax (char **mac)
2458 {
2459 char *answer;
2460 gboolean answer_bool;
2461 gboolean once_more;
2462 GError *error = NULL;
2463
2464 /* Ask for optional 'wimax' arguments. */
2465 printf (_("There is 1 optional argument for 'WiMax' connection type.\n"));
2466 answer = nmc_get_user_input (_("Do you want to provide it? (yes/no) [yes] "));
2467 if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2468 g_free (answer);
2469 return;
2470 }
2471
2472 if (!*mac) {
2473 do {
2474 *mac = nmc_get_user_input (_("MAC [none]: "));
2475 once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_ETHER, "mac", &error);
2476 if (once_more) {
2477 printf ("%s\n", error->message);
2478 g_clear_error (&error);
2479 g_free (*mac);
2480 }
2481 } while (once_more);
2482 }
2483
2484 g_free (answer);
2485 return;
2486 }
2487
2488 static void
2489 do_questionnaire_mobile (char **user, char **password)
2490 {
2491 char *answer;
2492 gboolean answer_bool;
2493
2494 /* Ask for optional 'gsm' or 'cdma' arguments. */
2495 printf (_("There are 2 optional arguments for 'mobile broadband' connection type.\n"));
2496 answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2497 if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2498 g_free (answer);
2499 return;
2500 }
2501
2502 if (!*user)
2503 *user = nmc_get_user_input (_("Username [none]: "));
2504 if (!*password)
2505 *password = nmc_get_user_input (_("Password [none]: "));
2506
2507 g_free (answer);
2508 return;
2509 }
2510
2511 static void
2512 do_questionnaire_bluetooth (char **bt_type)
2513 {
2514 char *answer;
2515 gboolean answer_bool;
2516 gboolean once_more;
2517
2518 /* Ask for optional 'bluetooth' arguments. */
2519 printf (_("There is 1 optional argument for 'bluetooth' connection type.\n"));
2520 answer = nmc_get_user_input (_("Do you want to provide it? (yes/no) [yes] "));
2521 if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2522 g_free (answer);
2523 return;
2524 }
2525
2526 if (!*bt_type) {
2527 do {
2528 *bt_type = nmc_get_user_input (_("Bluetooth type (panu, dun-gsm or dun-cdma) [panu]: "));
2529 if (!*bt_type)
2530 *bt_type = g_strdup ("panu");
2531 once_more = strcmp (*bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
2532 && strcmp (*bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")
2533 && strcmp (*bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")
2534 && strcmp (*bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU);
2535 if (once_more) {
2536 printf (_("Error: 'bt-type': '%s' is not a valid bluetooth type.\n"), *bt_type);
2537 g_free (*bt_type);
2538 }
2539 } while (once_more);
2540 }
2541
2542 g_free (answer);
2543 return;
2544 }
2545
2546 static void
2547 do_questionnaire_vlan (char **mtu, char **flags, char **ingress, char **egress)
2548 {
2549 char *answer;
2550 gboolean answer_bool;
2551 gboolean once_more;
2552 GError *error = NULL;
2553
2554 /* Ask for optional 'vlan' arguments. */
2555 printf (_("There are 4 optional arguments for 'VLAN' connection type.\n"));
2556 answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2557 if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2558 g_free (answer);
2559 return;
2560 }
2561
2562 if (!*mtu) {
2563 do {
2564 *mtu = nmc_get_user_input (_("MTU [auto]: "));
2565 once_more = !check_and_convert_mtu (*mtu, NULL, &error);
2566 if (once_more) {
2567 printf ("%s\n", error->message);
2568 g_clear_error (&error);
2569 g_free (*mtu);
2570 }
2571 } while (once_more);
2572 }
2573 if (!*flags) {
2574 do {
2575 *flags = nmc_get_user_input (_("VLAN flags (<0-7>) [none]: "));
2576 once_more = !check_and_convert_vlan_flags (*flags, NULL, &error);
2577 if (once_more) {
2578 printf ("%s\n", error->message);
2579 g_clear_error (&error);
2580 g_free (*flags);
2581 }
2582 } while (once_more);
2583 }
2584 if (!*ingress) {
2585 do {
2586 *ingress = nmc_get_user_input (_("Ingress priority maps [none]: "));
2587 once_more = !check_and_convert_vlan_prio_maps (*ingress, NM_VLAN_INGRESS_MAP, NULL, &error);
2588 if (once_more) {
2589 printf ("%s\n", error->message);
2590 g_clear_error (&error);
2591 g_free (*ingress);
2592 }
2593 } while (once_more);
2594 }
2595 if (!*egress) {
2596 do {
2597 *egress = nmc_get_user_input (_("Egress priority maps [none]: "));
2598 once_more = !check_and_convert_vlan_prio_maps (*egress, NM_VLAN_EGRESS_MAP, NULL, &error);
2599 if (once_more) {
2600 printf ("%s\n", error->message);
2601 g_clear_error (&error);
2602 g_free (*egress);
2603 }
2604 } while (once_more);
2605 }
2606
2607 g_free (answer);
2608 return;
2609 }
2610
2611 static void
2612 do_questionnaire_bond (char **mode, char **primary, char **miimon,
2613 char **downdelay, char **updelay,
2614 char **arpinterval, char **arpiptarget)
2615 {
2616 char *answer, *monitor_mode;
2617 gboolean answer_bool;
2618 unsigned long tmp;
2619 gboolean once_more;
2620 GError *error = NULL;
2621
2622 /* Ask for optional 'bond' arguments. */
2623 printf (_("There are optional arguments for 'bond' connection type.\n"));
2624 answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2625 if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2626 g_free (answer);
2627 return;
2628 }
2629
2630 if (!*mode) {
2631 const char *mode_tmp;
2632 do {
2633 *mode = nmc_get_user_input (_("Bonding mode [balance-rr]: "));
2634 if (!*mode)
2635 *mode = g_strdup ("balance-rr");
2636 mode_tmp = nmc_bond_validate_mode (*mode, &error);
2637 g_free (*mode);
2638 if (mode_tmp) {
2639 *mode = g_strdup (mode_tmp);
2640 } else {
2641 printf ("%s\n", error->message);
2642 g_clear_error (&error);
2643 }
2644 } while (!mode_tmp);
2645 }
2646
2647 if (g_strcmp0 (*mode, "active-backup") == 0 && !*primary) {
2648 do {
2649 *primary = nmc_get_user_input (_("Bonding primary interface [none]: "));
2650 once_more = *primary && !nm_utils_iface_valid_name (*primary);
2651 if (once_more) {
2652 printf (_("Error: 'primary': '%s' is not a valid interface name.\n"),
2653 *primary);
2654 g_free (*primary);
2655 }
2656 } while (once_more);
2657 }
2658
2659 do {
2660 monitor_mode = nmc_get_user_input (_("Bonding monitoring mode (miimon or arp) [miimon]: "));
2661 if (!monitor_mode)
2662 monitor_mode = g_strdup ("miimon");
2663 once_more = strcmp (monitor_mode, "miimon") && strcmp (monitor_mode, "arp");
2664 if (once_more) {
2665 printf (_("Error: '%s' is not a valid monitoring mode; use '%s' or '%s'.\n"),
2666 monitor_mode, "miimon", "arp");
2667 g_free (monitor_mode);
2668 }
2669 } while (once_more);
2670
2671 if (strcmp (monitor_mode, "miimon") == 0) {
2672 if (!*miimon) {
2673 do {
2674 *miimon = nmc_get_user_input (_("Bonding miimon [100]: "));
2675 once_more = *miimon && !nmc_string_to_uint (*miimon, TRUE, 0, G_MAXUINT32, &tmp);
2676 if (once_more) {
2677 printf (_("Error: 'miimon': '%s' is not a valid number <0-%u>.\n"),
2678 *miimon, G_MAXUINT32);
2679 g_free (*miimon);
2680 }
2681 } while (once_more);
2682 }
2683 if (!*downdelay) {
2684 do {
2685 *downdelay = nmc_get_user_input (_("Bonding downdelay [0]: "));
2686 once_more = *downdelay && !nmc_string_to_uint (*downdelay, TRUE, 0, G_MAXUINT32, &tmp);
2687 if (once_more) {
2688 printf (_("Error: 'downdelay': '%s' is not a valid number <0-%u>.\n"),
2689 *downdelay, G_MAXUINT32);
2690 g_free (*downdelay);
2691 }
2692 } while (once_more);
2693 }
2694 if (!*updelay) {
2695 do {
2696 *updelay = nmc_get_user_input (_("Bonding updelay [0]: "));
2697 once_more = *updelay && !nmc_string_to_uint (*updelay, TRUE, 0, G_MAXUINT32, &tmp);
2698 if (once_more) {
2699 printf (_("Error: 'updelay': '%s' is not a valid number <0-%u>.\n"),
2700 *updelay, G_MAXUINT32);
2701 g_free (*updelay);
2702 }
2703 } while (once_more);
2704 }
2705 } else {
2706 if (!*arpinterval) {
2707 do {
2708 *arpinterval = nmc_get_user_input (_("Bonding arp-interval [0]: "));
2709 once_more = *arpinterval && !nmc_string_to_uint (*arpinterval, TRUE, 0, G_MAXUINT32, &tmp);
2710 if (once_more) {
2711 printf (_("Error: 'arp-interval': '%s' is not a valid number <0-%u>.\n"),
2712 *arpinterval, G_MAXUINT32);
2713 g_free (*arpinterval);
2714 }
2715 } while (once_more);
2716 }
2717 if (!*arpiptarget) {
2718 //FIXME: verify the string
2719 *arpiptarget = nmc_get_user_input (_("Bonding arp-ip-target [none]: "));
2720 }
2721 }
2722
2723 g_free (answer);
2724 g_free (monitor_mode);
2725 return;
2726 }
2727
2728 static void
2729 do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay,
2730 char **hello_time, char **max_age, char **ageing_time)
2731 {
2732 char *answer;
2733 gboolean answer_bool;
2734 unsigned long tmp;
2735 gboolean once_more;
2736 GError *error = NULL;
2737
2738 /* Ask for optional 'bridge' arguments. */
2739 printf (_("There are 6 optional arguments for 'bridge' connection type.\n"));
2740 answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2741 if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2742 g_free (answer);
2743 return;
2744 }
2745
2746 if (!*stp) {
2747 gboolean stp_bool;
2748 do {
2749 *stp = nmc_get_user_input (_("Enable STP (yes/no) [yes]: "));
2750 *stp = *stp ? *stp : g_strdup ("yes");
2751 once_more = !nmc_string_to_bool (*stp, &stp_bool, &error);
2752 if (once_more) {
2753 printf (_("Error: 'stp': '%s'.\n"), error->message);
2754 g_clear_error (&error);
2755 g_free (*stp);
2756 }
2757 } while (once_more);
2758 }
2759 if (!*priority) {
2760 do {
2761 *priority = nmc_get_user_input (_("STP priority [128]: "));
2762 *priority = *priority ? *priority : g_strdup ("128");
2763 once_more = !nmc_string_to_uint (*priority, TRUE, 0, G_MAXUINT16, &tmp);
2764 if (once_more) {
2765 printf (_("Error: 'priority': '%s' is not a valid number <0-%d>.\n"),
2766 *priority, G_MAXUINT16);
2767 g_free (*priority);
2768 }
2769 } while (once_more);
2770 }
2771 if (!*fwd_delay) {
2772 do {
2773 *fwd_delay = nmc_get_user_input (_("Forward delay [15]: "));
2774 *fwd_delay = *fwd_delay ? *fwd_delay : g_strdup ("15");
2775 once_more = !nmc_string_to_uint (*fwd_delay, TRUE, 2, 30, &tmp);
2776 if (once_more) {
2777 printf (_("Error: 'forward-delay': '%s' is not a valid number <2-30>.\n"),
2778 *fwd_delay);
2779 g_free (*fwd_delay);
2780 }
2781 } while (once_more);
2782 }
2783
2784 if (!*hello_time) {
2785 do {
2786 *hello_time = nmc_get_user_input (_("Hello time [2]: "));
2787 *hello_time = *hello_time ? *hello_time : g_strdup ("2");
2788 once_more = !nmc_string_to_uint (*hello_time, TRUE, 1, 10, &tmp);
2789 if (once_more) {
2790 printf (_("Error: 'hello-time': '%s' is not a valid number <1-10>.\n"),
2791 *hello_time);
2792 g_free (*hello_time);
2793 }
2794 } while (once_more);
2795 }
2796 if (!*max_age) {
2797 do {
2798 *max_age = nmc_get_user_input (_("Max age [20]: "));
2799 *max_age = *max_age ? *max_age : g_strdup ("20");
2800 once_more = !nmc_string_to_uint (*max_age, TRUE, 6, 40, &tmp);
2801 if (once_more) {
2802 printf (_("Error: 'max-age': '%s' is not a valid number <6-40>.\n"),
2803 *max_age);
2804 g_free (*max_age);
2805 }
2806 } while (once_more);
2807 }
2808 if (!*ageing_time) {
2809 do {
2810 *ageing_time = nmc_get_user_input (_("MAC address ageing time [300]: "));
2811 *ageing_time = *ageing_time ? *ageing_time : g_strdup ("300");
2812 once_more = !nmc_string_to_uint (*ageing_time, TRUE, 0, 1000000, &tmp);
2813 if (once_more) {
2814 printf (_("Error: 'ageing-time': '%s' is not a valid number <0-1000000>.\n"),
2815 *ageing_time);
2816 g_free (*ageing_time);
2817 }
2818 } while (once_more);
2819 }
2820
2821 g_free (answer);
2822 return;
2823 }
2824
2825 static void
2826 do_questionnaire_bridge_slave (char **priority, char **path_cost, char **hairpin)
2827 {
2828 char *answer;
2829 gboolean answer_bool;
2830 unsigned long tmp;
2831 gboolean once_more;
2832 GError *error = NULL;
2833
2834 /* Ask for optional 'bridge-slave' arguments. */
2835 printf (_("There are 3 optional arguments for 'bridge-slave' connection type.\n"));
2836 answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2837 if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2838 g_free (answer);
2839 return;
2840 }
2841
2842 if (!*priority) {
2843 do {
2844 *priority = nmc_get_user_input (_("Bridge port priority [32]: "));
2845 *priority = *priority ? *priority : g_strdup ("32");
2846 once_more = !bridge_prop_string_to_uint (*priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
2847 NM_SETTING_BRIDGE_PORT_PRIORITY, &tmp, &error);
2848 if (once_more) {
2849 printf ("%s\n", error->message);
2850 g_clear_error (&error);
2851 g_free (*priority);
2852 }
2853 } while (once_more);
2854 }
2855 if (!*path_cost) {
2856 do {
2857 *path_cost = nmc_get_user_input (_("Bridge port STP path cost [100]: "));
2858 *path_cost = *path_cost ? *path_cost : g_strdup ("100");
2859 once_more = !bridge_prop_string_to_uint (*path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
2860 NM_SETTING_BRIDGE_PORT_PATH_COST, &tmp, &error);
2861 if (once_more) {
2862 printf ("%s\n", error->message);
2863 g_clear_error (&error);
2864 g_free (*path_cost);
2865 }
2866 } while (once_more);
2867 }
2868 if (!*hairpin) {
2869 gboolean hairpin_bool;
2870 do {
2871 *hairpin = nmc_get_user_input (_("Hairpin (yes/no) [yes]: "));
2872 *hairpin = *hairpin ? *hairpin : g_strdup ("yes");
2873 once_more = !nmc_string_to_bool (*hairpin, &hairpin_bool, &error);
2874 if (once_more) {
2875 printf (_("Error: 'hairpin': '%s'.\n"), error->message);
2876 g_clear_error (&error);
2877 g_free (*hairpin);
2878 }
2879 } while (once_more);
2880 }
2881
2882 g_free (answer);
2883 return;
2884 }
2885
2886 static void
2887 do_questionnaire_vpn (char **user)
2888 {
2889 char *answer;
2890 gboolean answer_bool;
2891
2892 /* Ask for optional 'vpn' arguments. */
2893 printf (_("There is 1 optional argument for 'VPN' connection type.\n"));
2894 answer = nmc_get_user_input (_("Do you want to provide it? (yes/no) [yes] "));
2895 if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2896 g_free (answer);
2897 return;
2898 }
2899
2900 if (!*user)
2901 *user = nmc_get_user_input (_("Username [none]: "));
2902
2903 g_free (answer);
2904 return;
2905 }
2906
2907 static void
2908 do_questionnaire_olpc (char **channel, char **dhcp_anycast)
2909 {
2910 char *answer;
2911 gboolean answer_bool;
2912 unsigned long tmp;
2913 gboolean once_more;
2914 GError *error = NULL;
2915
2916 /* Ask for optional 'olpc' arguments. */
2917 printf (_("There are 2 optional arguments for 'OLPC Mesh' connection type.\n"));
2918 answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] "));
2919 if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
2920 g_free (answer);
2921 return;
2922 }
2923
2924 if (!*channel) {
2925 do {
2926 *channel = nmc_get_user_input (_("OLPC Mesh channel [1]: "));
2927 once_more = *channel && !nmc_string_to_uint (*channel, TRUE, 1, 13, &tmp);
2928 if (once_more) {
2929 printf (_("Error: 'channel': '%s' is not a valid number <1-13>.\n"),
2930 *channel);
2931 g_free (*channel);
2932 }
2933 } while (once_more);
2934 }
2935 if (!*dhcp_anycast) {
2936 do {
2937 *dhcp_anycast = nmc_get_user_input (_("DHCP anycast MAC address [none]: "));
2938 once_more = !check_and_convert_mac (*dhcp_anycast, NULL, ARPHRD_ETHER, "dhcp-anycast", &error);
2939 if (once_more) {
2940 printf ("%s\n", error->message);
2941 g_clear_error (&error);
2942 g_free (*dhcp_anycast);
2943 }
2944 } while (once_more);
2945 }
2946
2947 g_free (answer);
2948 return;
2949 }
2950
2951 static gboolean
2952 split_address (char* str, char **ip, char **gw, char **rest)
2953 {
2954 size_t n1, n2, n3, n4, n5;
2955
2956 *ip = *gw = *rest = NULL;
2957 if (!str)
2958 return FALSE;
2959
2960 n1 = strspn (str, " \t");
2961 n2 = strcspn (str+n1, " \t\0") + n1;
2962 n3 = strspn (str+n2, " \t") + n2;
2963 n4 = strcspn (str+n3, " \t\0") + n3;
2964 n5 = strspn (str+n4, " \t") + n4;
2965
2966 str[n2] = str[n4] = '\0';
2967 *ip = str[n1] ? str + n1 : NULL;
2968 *gw = str[n3] ? str + n3 : NULL;
2969 *rest = str[n5] ? str + n5 : NULL;
2970
2971 return TRUE;
2972 }
2973
2974 static void
2975 ask_for_ip_addresses (NMConnection *connection, int family)
2976 {
2977 gboolean ip_loop;
2978 GError *error = NULL;
2979 char *str, *ip, *gw, *rest;
2980 const char *prompt;
2981 gboolean added;
2982 gpointer ipaddr;
2983
2984 if (family == 4)
2985 prompt =_("IPv4 address (IP[/plen] [gateway]) [none]: ");
2986 else
2987 prompt =_("IPv6 address (IP[/plen] [gateway]) [none]: ");
2988
2989 ip_loop = TRUE;
2990 do {
2991 str = nmc_get_user_input (prompt);
2992 split_address (str, &ip, &gw, &rest);
2993 if (ip) {
2994 if (family == 4)
2995 ipaddr = nmc_parse_and_build_ip4_address (ip, gw, &error);
2996 else
2997 ipaddr = nmc_parse_and_build_ip6_address (ip, gw, &error);
2998 if (ipaddr) {
2999 if (family == 4)
3000 added = add_ip4_address_to_connection ((NMIP4Address *) ipaddr, connection);
3001 else
3002 added = add_ip6_address_to_connection ((NMIP6Address *) ipaddr, connection);
3003 gw = gw ? gw : (family == 4) ? "0.0.0.0" : "::";
3004 if (added)
3005 printf (_(" Address successfully added: %s %s\n"), ip, gw);
3006 else
3007 printf (_(" Warning: address already present: %s %s\n"), ip, gw);
3008 if (rest)
3009 printf (_(" Warning: ignoring garbage at the end: '%s'\n"), rest);
3010 } else {
3011 g_prefix_error (&error, _("Error: "));
3012 printf ("%s\n", error->message);
3013 g_clear_error (&error);
3014 }
3015 } else
3016 ip_loop = FALSE;
3017
3018 g_free (str);
3019 } while (ip_loop);
3020 }
3021
3022 static void
3023 do_questionnaire_ip (NMConnection *connection)
3024 {
3025 char *answer;
3026 gboolean answer_bool;
3027
3028 /* Ask for IP addresses */
3029 answer = nmc_get_user_input (_("Do you want to add IP addresses? (yes/no) [yes] "));
3030 if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) {
3031 g_free (answer);
3032 return;
3033 }
3034
3035 printf (_("Press <Enter> to finish adding addresses.\n"));
3036
3037 ask_for_ip_addresses (connection, 4);
3038 ask_for_ip_addresses (connection, 6);
3039
3040 g_free (answer);
3041 return;
3042 }
3043
3044 static gboolean
3045 complete_connection_by_type (NMConnection *connection,
3046 const char *con_type,
3047 GSList *all_connections,
3048 gboolean ask,
3049 int argc,
3050 char **argv,
3051 GError **error)
3052 {
3053 NMSettingConnection *s_con;
3054 NMSettingWired *s_wired;
3055 NMSettingInfiniband *s_infiniband;
3056 NMSettingWireless *s_wifi;
3057 NMSettingWimax *s_wimax;
3058 NMSettingGsm *s_gsm;
3059 NMSettingCdma *s_cdma;
3060 NMSettingBluetooth *s_bt;
3061 NMSettingVlan *s_vlan;
3062 NMSettingBond *s_bond;
3063 NMSettingTeam *s_team;
3064 NMSettingTeamPort *s_team_port;
3065 NMSettingBridge *s_bridge;
3066 NMSettingBridgePort *s_bridge_port;
3067 NMSettingVPN *s_vpn;
3068 NMSettingOlpcMesh *s_olpc_mesh;
3069
3070 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3071
3072 s_con = nm_connection_get_setting_connection (connection);
3073 g_assert (s_con);
3074
3075 if (!strcmp (con_type, NM_SETTING_WIRED_SETTING_NAME)) {
3076 /* Build up the settings required for 'ethernet' */
3077 gboolean success = FALSE;
3078 const char *mtu_c = NULL;
3079 char *mtu = NULL;
3080 guint32 mtu_int = 0;
3081 const char *mac_c = NULL;
3082 char *mac = NULL;
3083 const char *cloned_mac_c = NULL;
3084 char *cloned_mac = NULL;
3085 GByteArray *array = NULL;
3086 GByteArray *cloned_array = NULL;
3087 nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE},
3088 {"mac", TRUE, &mac_c, FALSE},
3089 {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
3090 {NULL} };
3091
3092 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3093 return FALSE;
3094
3095 /* Also ask for all optional arguments if '--ask' is specified. */
3096 mtu = mtu_c ? g_strdup (mtu_c) : NULL;
3097 mac = mac_c ? g_strdup (mac_c) : NULL;
3098 cloned_mac = cloned_mac_c ? g_strdup (cloned_mac_c) : NULL;
3099 if (ask)
3100 do_questionnaire_ethernet (TRUE, &mtu, &mac, &cloned_mac);
3101
3102 if (!check_and_convert_mtu (mtu, &mtu_int, error))
3103 goto cleanup_wired;
3104 if (!check_and_convert_mac (mac, &array, ARPHRD_ETHER, "mac", error))
3105 goto cleanup_wired;
3106 if (!check_and_convert_mac (cloned_mac, &cloned_array, ARPHRD_ETHER, "cloned-mac", error))
3107 goto cleanup_wired;
3108
3109 /* Add ethernet setting */
3110 s_wired = (NMSettingWired *) nm_setting_wired_new ();
3111 nm_connection_add_setting (connection, NM_SETTING (s_wired));
3112
3113 if (mtu)
3114 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
3115 if (array)
3116 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, array, NULL);
3117 if (cloned_array)
3118 g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_array, NULL);
3119
3120 success = TRUE;
3121 cleanup_wired:
3122 g_free (mtu);
3123 g_free (mac);
3124 g_free (cloned_mac);
3125 if (array)
3126 g_byte_array_free (array, TRUE);
3127 if (cloned_array)
3128 g_byte_array_free (cloned_array, TRUE);
3129 if (!success)
3130 return FALSE;
3131
3132 } else if (!strcmp (con_type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
3133 /* Build up the settings required for 'infiniband' */
3134 gboolean success = FALSE;
3135 const char *mtu_c = NULL;
3136 char *mtu = NULL;
3137 guint32 mtu_int = 0;
3138 const char *mac_c = NULL;
3139 char *mac = NULL;
3140 GByteArray *array = NULL;
3141 const char *mode_c = NULL;
3142 char *mode = NULL;
3143 const char *parent_c = NULL;
3144 char *parent = NULL;
3145 const char *p_key_c = NULL;
3146 char *p_key = NULL;
3147 guint32 p_key_int = 0;
3148 nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE},
3149 {"mac", TRUE, &mac_c, FALSE},
3150 {"transport-mode", TRUE, &mode_c, FALSE},
3151 {"parent", TRUE, &parent_c, FALSE},
3152 {"p-key", TRUE, &p_key_c, FALSE},
3153 {NULL} };
3154
3155 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3156 return FALSE;
3157
3158 /* Also ask for all optional arguments if '--ask' is specified. */
3159 mtu = mtu_c ? g_strdup (mtu_c) : NULL;
3160 mac = mac_c ? g_strdup (mac_c) : NULL;
3161 mode = mode_c ? g_strdup (mode_c) : NULL;
3162 parent = parent_c ? g_strdup (parent_c) : NULL;
3163 p_key = p_key_c ? g_strdup (p_key_c) : NULL;
3164 if (ask)
3165 do_questionnaire_infiniband (&mtu, &mac, &mode, &parent, &p_key);
3166
3167 if (!check_and_convert_mtu (mtu, &mtu_int, error))
3168 goto cleanup_ib;
3169 if (!check_and_convert_mac (mac, &array, ARPHRD_INFINIBAND, "mac", error))
3170 goto cleanup_ib;
3171 if (!check_infiniband_mode (mode, error))
3172 goto cleanup_ib;
3173 if (p_key) {
3174 if (!check_infiniband_p_key (p_key, &p_key_int, error))
3175 goto cleanup_ib;
3176 if (!check_infiniband_parent (parent, error))
3177 goto cleanup_ib;
3178 } else if (parent) {
3179 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3180 _("Error: 'parent': not valid without 'p-key'."));
3181 goto cleanup_ib;
3182 }
3183
3184 /* Add 'infiniband' setting */
3185 s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new ();
3186 nm_connection_add_setting (connection, NM_SETTING (s_infiniband));
3187
3188 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, mode ? mode : "datagram", NULL);
3189 if (mtu)
3190 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu_int, NULL);
3191 if (array) {
3192 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, array, NULL);
3193 g_byte_array_free (array, TRUE);
3194 }
3195 if (p_key)
3196 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_P_KEY, p_key_int, NULL);
3197 if (parent)
3198 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_PARENT, parent, NULL);
3199
3200
3201 success = TRUE;
3202 cleanup_ib:
3203 g_free (mtu);
3204 g_free (mac);
3205 g_free (mode);
3206 g_free (parent);
3207 g_free (p_key);
3208 if (!success)
3209 return FALSE;
3210
3211 } else if (!strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME)) {
3212 /* Build up the settings required for 'wifi' */
3213 gboolean success = FALSE;
3214 char *ssid_ask = NULL;
3215 const char *ssid = NULL;
3216 GByteArray *ssid_arr = NULL;
3217 const char *mtu_c = NULL;
3218 char *mtu = NULL;
3219 guint32 mtu_int = 0;
3220 const char *mac_c = NULL;
3221 char *mac = NULL;
3222 GByteArray *mac_array = NULL;
3223 const char *cloned_mac_c = NULL;
3224 char *cloned_mac = NULL;
3225 GByteArray *cloned_mac_array = NULL;
3226 nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask},
3227 {"mtu", TRUE, &mtu_c, FALSE},
3228 {"mac", TRUE, &mac_c, FALSE},
3229 {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
3230 {NULL} };
3231
3232 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3233 return FALSE;
3234
3235 if (!ssid && ask)
3236 ssid = ssid_ask = nmc_get_user_input (_("SSID: "));
3237 if (!ssid) {
3238 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3239 _("Error: 'ssid' is required."));
3240 return FALSE;
3241 }
3242
3243 /* Also ask for all optional arguments if '--ask' is specified. */
3244 mtu = mtu_c ? g_strdup (mtu_c) : NULL;
3245 mac = mac_c ? g_strdup (mac_c) : NULL;
3246 cloned_mac = cloned_mac_c ? g_strdup (cloned_mac_c) : NULL;
3247 if (ask)
3248 do_questionnaire_wifi (&mtu, &mac, &cloned_mac);
3249
3250 if (!check_and_convert_mtu (mtu, &mtu_int, error))
3251 goto cleanup_wifi;
3252 if (!check_and_convert_mac (mac, &mac_array, ARPHRD_ETHER, "mac", error))
3253 goto cleanup_wifi;
3254 if (!check_and_convert_mac (cloned_mac, &cloned_mac_array, ARPHRD_ETHER, "cloned-mac", error))
3255 goto cleanup_wifi;
3256
3257 /* Add wifi setting */
3258 s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
3259 nm_connection_add_setting (connection, NM_SETTING (s_wifi));
3260
3261 ssid_arr = g_byte_array_sized_new (strlen (ssid));
3262 g_byte_array_append (ssid_arr, (const guint8 *) ssid, strlen (ssid));
3263 g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid_arr, NULL);
3264
3265 if (mtu)
3266 g_object_set (s_wifi, NM_SETTING_WIRELESS_MTU, mtu_int, NULL);
3267 if (mac_array)
3268 g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, mac_array, NULL);
3269 if (cloned_mac_array)
3270 g_object_set (s_wifi, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, cloned_mac_array, NULL);
3271
3272 success = TRUE;
3273 cleanup_wifi:
3274 g_free (ssid_ask);
3275 g_free (mtu);
3276 g_free (mac);
3277 g_free (cloned_mac);
3278 if (ssid_arr)
3279 g_byte_array_free (ssid_arr, TRUE);
3280 if (mac_array)
3281 g_byte_array_free (mac_array, TRUE);
3282 if (cloned_mac_array)
3283 g_byte_array_free (cloned_mac_array, TRUE);
3284 if (!success)
3285 return FALSE;
3286
3287 } else if (!strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)) {
3288 /* Build up the settings required for 'wimax' */
3289 gboolean success = FALSE;
3290 const char *nsp_name = NULL;
3291 char *nsp_name_ask = NULL;
3292 const char *mac_c = NULL;
3293 char *mac = NULL;
3294 GByteArray *mac_array = NULL;
3295 nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !ask},
3296 {"mac", TRUE, &mac_c, FALSE},
3297 {NULL} };
3298
3299 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3300 return FALSE;
3301
3302 if (!nsp_name && ask)
3303 nsp_name = nsp_name_ask = nmc_get_user_input (_("WiMAX NSP name: "));
3304 if (!nsp_name) {
3305 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3306 _("Error: 'nsp' is required."));
3307 goto cleanup_wimax;
3308 }
3309
3310 /* Also ask for all optional arguments if '--ask' is specified. */
3311 mac = mac_c ? g_strdup (mac_c) : NULL;
3312 if (ask)
3313 do_questionnaire_wimax (&mac);
3314
3315 if (!check_and_convert_mac (mac, &mac_array, ARPHRD_ETHER, "mac", error))
3316 goto cleanup_wimax;
3317
3318 /* Add 'wimax' setting */
3319 s_wimax = (NMSettingWimax *) nm_setting_wimax_new ();
3320 nm_connection_add_setting (connection, NM_SETTING (s_wimax));
3321 g_object_set (s_wimax, NM_SETTING_WIMAX_NETWORK_NAME, nsp_name, NULL);
3322
3323 if (mac_array) {
3324 g_object_set (s_wimax, NM_SETTING_WIMAX_MAC_ADDRESS, mac_array, NULL);
3325 g_byte_array_free (mac_array, TRUE);
3326 }
3327
3328 success = TRUE;
3329 cleanup_wimax:
3330 g_free (nsp_name_ask);
3331 g_free (mac);
3332 if (!success)
3333 return FALSE;
3334
3335 } else if ( !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME)
3336 || !strcmp (con_type, NM_SETTING_CDMA_SETTING_NAME)) {
3337 /* Build up the settings required for 'gsm' or 'cdma' mobile broadband */
3338 gboolean success = FALSE;
3339 const char *apn = NULL;
3340 char *apn_ask = NULL;
3341 const char *user_c = NULL;
3342 char *user = NULL;
3343 const char *password_c = NULL;
3344 char *password = NULL;
3345 gboolean is_gsm;
3346 int i = 0;
3347 nmc_arg_t gsm_args[] = { {NULL}, {NULL}, {NULL}, /* placeholders */
3348 {NULL} };
3349
3350 is_gsm = !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME);
3351
3352 if (is_gsm)
3353 gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !ask};
3354 gsm_args[i++] = (nmc_arg_t) {"user", TRUE, &user_c, FALSE};
3355 gsm_args[i++] = (nmc_arg_t) {"password", TRUE, &password_c, FALSE};
3356 gsm_args[i++] = (nmc_arg_t) {NULL};
3357
3358 if (!nmc_parse_args (gsm_args, FALSE, &argc, &argv, error))
3359 return FALSE;
3360
3361 if (!apn && ask && is_gsm)
3362 apn = apn_ask = nmc_get_user_input (_("APN: "));
3363 if (!apn && is_gsm) {
3364 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3365 _("Error: 'apn' is required."));
3366 goto cleanup_mobile;
3367 }
3368
3369 /* Also ask for all optional arguments if '--ask' is specified. */
3370 user = user_c ? g_strdup (user_c) : NULL;
3371 password = password_c ? g_strdup (password_c) : NULL;
3372 if (ask)
3373 do_questionnaire_mobile (&user, &password);
3374
3375 if (is_gsm) {
3376 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, NULL);
3377
3378 /* Add 'gsm' setting */
3379 s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
3380 nm_connection_add_setting (connection, NM_SETTING (s_gsm));
3381 g_object_set (s_gsm,
3382 NM_SETTING_GSM_NUMBER, "*99#",
3383 NM_SETTING_GSM_APN, apn,
3384 NM_SETTING_GSM_USERNAME, user,
3385 NM_SETTING_GSM_PASSWORD, password,
3386 NULL);
3387 g_free (apn_ask);
3388 } else {
3389 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME, NULL);
3390
3391 /* Add 'cdma' setting */
3392 s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
3393 nm_connection_add_setting (connection, NM_SETTING (s_cdma));
3394 g_object_set (s_cdma,
3395 NM_SETTING_CDMA_NUMBER, "#777",
3396 NM_SETTING_CDMA_USERNAME, user,
3397 NM_SETTING_CDMA_PASSWORD, password,
3398 NULL);
3399 }
3400
3401 success = TRUE;
3402 cleanup_mobile:
3403 g_free (user);
3404 g_free (password);
3405 if (!success)
3406 return FALSE;
3407
3408 } else if (!strcmp (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
3409 /* Build up the settings required for 'bluetooth' */
3410 gboolean success = FALSE;
3411 const char *addr = NULL;
3412 char *addr_ask = NULL;
3413 const char *bt_type_c = NULL;
3414 char *bt_type = NULL;
3415 GByteArray *array = NULL;
3416 nmc_arg_t exp_args[] = { {"addr", TRUE, &addr, !ask},
3417 {"bt-type", TRUE, &bt_type_c, FALSE},
3418 {NULL} };
3419
3420 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3421 return FALSE;
3422
3423 if (!addr && ask)
3424 addr = addr_ask = nmc_get_user_input (_("Bluetooth device address: "));
3425 if (!addr) {
3426 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3427 _("Error: 'addr' is required."));
3428 return FALSE;
3429 }
3430 if (!check_and_convert_mac (addr, &array, ARPHRD_ETHER, "addr", error))
3431 goto cleanup_bt;
3432
3433 /* Also ask for all optional arguments if '--ask' is specified. */
3434 bt_type = bt_type_c ? g_strdup (bt_type_c) : NULL;
3435 if (ask)
3436 do_questionnaire_bluetooth (&bt_type);
3437
3438 /* Default to 'panu' if bt-type is not provided. */
3439 if (!bt_type)
3440 bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_PANU);
3441
3442 /* Add 'bluetooth' setting */
3443 s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new ();
3444 nm_connection_add_setting (connection, NM_SETTING (s_bt));
3445
3446 if (array) {
3447 g_object_set (s_bt, NM_SETTING_BLUETOOTH_BDADDR, array, NULL);
3448 g_byte_array_free (array, TRUE);
3449 }
3450
3451 /* 'dun' type requires adding 'gsm' or 'cdma' setting */
3452 if ( !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
3453 || !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) {
3454 bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
3455 s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
3456 nm_connection_add_setting (connection, NM_SETTING (s_gsm));
3457 g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
3458 // g_object_set (s_gsm, NM_SETTING_GSM_APN, "FIXME", NULL;
3459
3460 } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) {
3461 bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
3462 s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
3463 nm_connection_add_setting (connection, NM_SETTING (s_cdma));
3464 g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL);
3465
3466 } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) {
3467 /* no op */
3468 } else {
3469 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3470 _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."),
3471 bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN,
3472 NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma");
3473 goto cleanup_bt;
3474 }
3475 g_object_set (s_bt, NM_SETTING_BLUETOOTH_TYPE, bt_type, NULL);
3476
3477 success = TRUE;
3478 cleanup_bt:
3479 g_free (addr_ask);
3480 g_free (bt_type);
3481 if (!success)
3482 return FALSE;
3483
3484 } else if (!strcmp (con_type, NM_SETTING_VLAN_SETTING_NAME)) {
3485 /* Build up the settings required for 'vlan' */
3486 gboolean success = FALSE;
3487 const char *ifname = NULL;
3488 const char *parent = NULL;
3489 char *parent_ask = NULL;
3490 const char *vlan_id = NULL;
3491 char *vlan_id_ask = NULL;
3492 unsigned long id = 0;
3493 const char *flags_c = NULL;
3494 char *flags = NULL;
3495 guint32 flags_int = 0;
3496 const char *ingress_c = NULL, *egress_c = NULL;
3497 char *ingress = NULL, *egress = NULL;
3498 char **ingress_arr = NULL, **egress_arr = NULL, **p;
3499 const char *mtu_c = NULL;
3500 char *mtu = NULL;
3501 guint32 mtu_int;
3502 GByteArray *addr_array = NULL;
3503 nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask},
3504 {"id", TRUE, &vlan_id, !ask},
3505 {"flags", TRUE, &flags_c, FALSE},
3506 {"ingress", TRUE, &ingress_c, FALSE},
3507 {"egress", TRUE, &egress_c, FALSE},
3508 {"mtu", TRUE, &mtu_c, FALSE},
3509 {NULL} };
3510
3511 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3512 return FALSE;
3513
3514 if (!parent && ask)
3515 parent = parent_ask = nmc_get_user_input (_("VLAN parent device or connection UUID: "));
3516 if (!parent) {
3517 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3518 _("Error: 'dev' is required."));
3519 return FALSE;
3520 }
3521 if (!vlan_id && ask)
3522 vlan_id = vlan_id_ask = nmc_get_user_input (_("VLAN ID <0-4095>: "));
3523 if (!vlan_id) {
3524 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3525 _("Error: 'id' is required."));
3526 goto cleanup_vlan;
3527 }
3528 if (vlan_id) {
3529 if (!nmc_string_to_uint (vlan_id, TRUE, 0, 4095, &id)) {
3530 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3531 _("Error: 'id': '%s' is not valid; use <0-4095>."),
3532 vlan_id);
3533 goto cleanup_vlan;
3534 }
3535 }
3536
3537 if ( !(addr_array = nm_utils_hwaddr_atoba (parent, ARPHRD_ETHER))
3538 && !nm_utils_is_uuid (parent)
3539 && !nm_utils_iface_valid_name (parent)) {
3540 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3541 _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
3542 parent);
3543 goto cleanup_vlan;
3544 }
3545
3546 /* Also ask for all optional arguments if '--ask' is specified. */
3547 mtu = mtu_c ? g_strdup (mtu_c) : NULL;
3548 flags = flags_c ? g_strdup (flags_c) : NULL;
3549 ingress = ingress_c ? g_strdup (ingress_c) : NULL;
3550 egress = egress_c ? g_strdup (egress_c) : NULL;
3551 if (ask)
3552 do_questionnaire_vlan (&mtu, &flags, &ingress, &egress);
3553
3554 /* ifname is taken from connection's ifname */
3555 ifname = nm_setting_connection_get_interface_name (s_con);
3556
3557 if (!check_and_convert_mtu (mtu, &mtu_int, error))
3558 goto cleanup_vlan;
3559 if (!check_and_convert_vlan_flags (flags, &flags_int, error))
3560 goto cleanup_vlan;
3561 if (!check_and_convert_vlan_prio_maps (ingress, NM_VLAN_INGRESS_MAP, &ingress_arr, error))
3562 goto cleanup_vlan;
3563 if (!check_and_convert_vlan_prio_maps (egress, NM_VLAN_EGRESS_MAP, &ingress_arr, error))
3564 goto cleanup_vlan;
3565
3566 /* Add 'vlan' setting */
3567 s_vlan = (NMSettingVlan *) nm_setting_vlan_new ();
3568 nm_connection_add_setting (connection, NM_SETTING (s_vlan));
3569
3570 /* Add 'wired' setting if necessary */
3571 if (mtu || addr_array) {
3572 s_wired = (NMSettingWired *) nm_setting_wired_new ();
3573 nm_connection_add_setting (connection, NM_SETTING (s_wired));
3574
3575 if (mtu)
3576 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
3577 if (addr_array)
3578 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, addr_array, NULL);
3579 }
3580
3581 /* Set 'vlan' properties */
3582 if (!addr_array)
3583 g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL);
3584
3585 if (ifname)
3586 g_object_set (s_vlan, NM_SETTING_VLAN_INTERFACE_NAME, ifname, NULL);
3587 g_object_set (s_vlan, NM_SETTING_VLAN_ID, id, NULL);
3588
3589 if (flags)
3590 g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, flags_int, NULL);
3591 for (p = ingress_arr; p && *p; p++)
3592 nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, *p);
3593 for (p = egress_arr; p && *p; p++)
3594 nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, *p);
3595
3596 success = TRUE;
3597 cleanup_vlan:
3598 g_free (mtu);
3599 g_free (flags);
3600 g_free (ingress);
3601 g_free (egress);
3602 if (addr_array)
3603 g_byte_array_free (addr_array, TRUE);
3604 g_free (parent_ask);
3605 g_free (vlan_id_ask);
3606 g_strfreev (ingress_arr);
3607 g_strfreev (egress_arr);
3608 if (!success)
3609 return FALSE;
3610
3611 } else if (!strcmp (con_type, NM_SETTING_BOND_SETTING_NAME)) {
3612 /* Build up the settings required for 'bond' */
3613 gboolean success = FALSE;
3614 char *bond_ifname = NULL;
3615 const char *ifname = NULL;
3616 const char *bond_mode_c = NULL;
3617 char *bond_mode = NULL;
3618 const char *bond_primary_c = NULL;
3619 char *bond_primary = NULL;
3620 const char *bond_miimon_c = NULL;
3621 char *bond_miimon = NULL;
3622 const char *bond_downdelay_c = NULL;
3623 char *bond_downdelay = NULL;
3624 const char *bond_updelay_c = NULL;
3625 char *bond_updelay = NULL;
3626 const char *bond_arpinterval_c = NULL;
3627 char *bond_arpinterval = NULL;
3628 const char *bond_arpiptarget_c = NULL;
3629 char *bond_arpiptarget = NULL;
3630 nmc_arg_t exp_args[] = { {"mode", TRUE, &bond_mode_c, FALSE},
3631 {"primary", TRUE, &bond_primary_c, FALSE},
3632 {"miimon", TRUE, &bond_miimon_c, FALSE},
3633 {"downdelay", TRUE, &bond_downdelay_c, FALSE},
3634 {"updelay", TRUE, &bond_updelay_c, FALSE},
3635 {"arp-interval", TRUE, &bond_arpinterval_c, FALSE},
3636 {"arp-ip-target", TRUE, &bond_arpiptarget_c, FALSE},
3637 {NULL} };
3638
3639 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3640 return FALSE;
3641
3642 /* Also ask for all optional arguments if '--ask' is specified. */
3643 bond_mode = bond_mode_c ? g_strdup (bond_mode_c) : NULL;
3644 bond_primary = bond_primary_c ? g_strdup (bond_primary_c) : NULL;
3645 bond_miimon = bond_miimon_c ? g_strdup (bond_miimon_c) : NULL;
3646 bond_downdelay = bond_downdelay_c ? g_strdup (bond_downdelay_c) : NULL;
3647 bond_updelay = bond_updelay_c ? g_strdup (bond_updelay_c) : NULL;
3648 bond_arpinterval = bond_arpinterval_c ? g_strdup (bond_arpinterval_c) : NULL;
3649 bond_arpiptarget = bond_arpiptarget_c ? g_strdup (bond_arpiptarget_c) : NULL;
3650 if (ask)
3651 do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon,
3652 &bond_downdelay, &bond_updelay,
3653 &bond_arpinterval, &bond_arpiptarget);
3654
3655 /* Use connection's ifname as 'bond' ifname if exists, else generate one */
3656 ifname = nm_setting_connection_get_interface_name (s_con);
3657 if (!ifname)
3658 bond_ifname = unique_master_iface_ifname (all_connections,
3659 NM_SETTING_BOND_SETTING_NAME,
3660 NM_SETTING_BOND_INTERFACE_NAME,
3661 "nm-bond");
3662 else
3663 bond_ifname = g_strdup (ifname);
3664
3665 /* Add 'bond' setting */
3666 s_bond = (NMSettingBond *) nm_setting_bond_new ();
3667 nm_connection_add_setting (connection, NM_SETTING (s_bond));
3668
3669 /* Set bond options */
3670 g_object_set (s_bond, NM_SETTING_BOND_INTERFACE_NAME, bond_ifname, NULL);
3671 if (bond_mode) {
3672 GError *err = NULL;
3673 const char *bm;
3674 if (!(bm = nmc_bond_validate_mode (bond_mode, &err))) {
3675 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3676 _("Error: 'mode': %s."), err->message);
3677 g_clear_error (&err);
3678 goto cleanup_bond;
3679 }
3680 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, bm);
3681 }
3682 if (bond_primary) {
3683 if (!nm_utils_iface_valid_name (bond_primary)) {
3684 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3685 _("Error: 'primary': '%s' is not a valid interface name."),
3686 bond_primary);
3687 goto cleanup_bond;
3688 }
3689 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, bond_primary);
3690 }
3691 if (bond_miimon)
3692 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, bond_miimon);
3693 if (bond_downdelay && strcmp (bond_downdelay, "0") != 0)
3694 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, bond_downdelay);
3695 if (bond_updelay && strcmp (bond_updelay, "0") != 0)
3696 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, bond_updelay);
3697 if (bond_arpinterval && strcmp (bond_arpinterval, "0") != 0)
3698 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, bond_arpinterval);
3699 if (bond_arpiptarget)
3700 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, bond_arpiptarget);
3701
3702 success = TRUE;
3703 cleanup_bond:
3704 g_free (bond_ifname);
3705 g_free (bond_mode);
3706 g_free (bond_primary);
3707 g_free (bond_miimon);
3708 g_free (bond_downdelay);
3709 g_free (bond_updelay);
3710 g_free (bond_arpinterval);
3711 g_free (bond_arpiptarget);
3712 if (!success)
3713 return FALSE;
3714
3715 } else if (!strcmp (con_type, "bond-slave")) {
3716 /* Build up the settings required for 'bond-slave' */
3717 const char *master = NULL;
3718 char *master_ask = NULL;
3719 const char *type = NULL;
3720 nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask},
3721 {"type", TRUE, &type, FALSE},
3722 {NULL} };
3723
3724 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error))
3725 return FALSE;
3726
3727 if (!master && ask)
3728 master = master_ask = nmc_get_user_input (_("Bond master: "));
3729 if (!master) {
3730 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3731 _("Error: 'master' is required."));
3732 return FALSE;
3733 }
3734
3735 if (type)
3736 printf (_("Warning: 'type' is currently ignored. "
3737 "We only support ethernet slaves for now.\n"));
3738
3739 /* Change properties in 'connection' setting */
3740 g_object_set (s_con,
3741 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
3742 NM_SETTING_CONNECTION_MASTER, master,
3743 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME,
3744 NULL);
3745
3746 /* Add ethernet setting */
3747 s_wired = (NMSettingWired *) nm_setting_wired_new ();
3748 nm_connection_add_setting (connection, NM_SETTING (s_wired));
3749
3750 g_free (master_ask);
3751
3752 } else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) {
3753 /* Build up the settings required for 'team' */
3754 char *team_ifname = NULL;
3755 const char *ifname = NULL;
3756 const char *config = NULL;
3757 nmc_arg_t exp_args[] = { {"config", TRUE, &config, FALSE},
3758 {NULL} };
3759
3760 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3761 return FALSE;
3762
3763 /* Use connection's ifname as 'team' ifname if exists, else generate one */
3764 ifname = nm_setting_connection_get_interface_name (s_con);
3765 if (!ifname)
3766 team_ifname = unique_master_iface_ifname (all_connections,
3767 NM_SETTING_TEAM_SETTING_NAME,
3768 NM_SETTING_TEAM_INTERFACE_NAME,
3769 "nm-team");
3770 else
3771 team_ifname = g_strdup (ifname);
3772
3773 /* Add 'team' setting */
3774 s_team = (NMSettingTeam *) nm_setting_team_new ();
3775 nm_connection_add_setting (connection, NM_SETTING (s_team));
3776
3777 /* Set team options */
3778 g_object_set (s_team, NM_SETTING_TEAM_INTERFACE_NAME, team_ifname, NULL);
3779 if (config)
3780 g_object_set (s_team, NM_SETTING_TEAM_CONFIG, config, NULL);
3781
3782 g_free (team_ifname);
3783
3784 } else if (!strcmp (con_type, "team-slave")) {
3785 /* Build up the settings required for 'team-slave' */
3786 const char *master = NULL;
3787 char *master_ask = NULL;
3788 const char *type = NULL;
3789 const char *config = NULL;
3790 nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask},
3791 {"type", TRUE, &type, FALSE},
3792 {"config", TRUE, &config, FALSE},
3793 {NULL} };
3794
3795 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error))
3796 return FALSE;
3797
3798 if (!master && ask)
3799 master = master_ask = nmc_get_user_input (_("Team master: "));
3800 if (!master) {
3801 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3802 _("Error: 'master' is required."));
3803 return FALSE;
3804 }
3805
3806 if (type)
3807 printf (_("Warning: 'type' is currently ignored. "
3808 "We only support ethernet slaves for now.\n"));
3809
3810 /* Add 'team-port' setting */
3811 s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new ();
3812 nm_connection_add_setting (connection, NM_SETTING (s_team_port));
3813
3814 if (config)
3815 g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, config, NULL);
3816
3817 /* Change properties in 'connection' setting */
3818 g_object_set (s_con,
3819 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
3820 NM_SETTING_CONNECTION_MASTER, master,
3821 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME,
3822 NULL);
3823
3824 /* Add ethernet setting */
3825 s_wired = (NMSettingWired *) nm_setting_wired_new ();
3826 nm_connection_add_setting (connection, NM_SETTING (s_wired));
3827
3828 g_free (master_ask);
3829
3830 } else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
3831 /* Build up the settings required for 'bridge' */
3832 gboolean success = FALSE;
3833 char *bridge_ifname = NULL;
3834 const char *ifname = NULL;
3835 const char *stp_c = NULL;
3836 char *stp = NULL;
3837 const char *priority_c = NULL;
3838 char *priority = NULL;
3839 const char *fwd_delay_c = NULL;
3840 char *fwd_delay = NULL;
3841 const char *hello_time_c = NULL;
3842 char *hello_time = NULL;
3843 const char *max_age_c = NULL;
3844 char *max_age = NULL;
3845 const char *ageing_time_c = NULL;
3846 char *ageing_time = NULL;
3847 gboolean stp_bool;
3848 unsigned long stp_prio_int, fwd_delay_int, hello_time_int,
3849 max_age_int, ageing_time_int;
3850 nmc_arg_t exp_args[] = { {"stp", TRUE, &stp_c, FALSE},
3851 {"priority", TRUE, &priority_c, FALSE},
3852 {"forward-delay", TRUE, &fwd_delay_c, FALSE},
3853 {"hello-time", TRUE, &hello_time_c, FALSE},
3854 {"max-age", TRUE, &max_age_c, FALSE},
3855 {"ageing-time", TRUE, &ageing_time_c, FALSE},
3856 {NULL} };
3857
3858 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
3859 return FALSE;
3860
3861 /* Also ask for all optional arguments if '--ask' is specified. */
3862 stp = stp_c ? g_strdup (stp_c) : NULL;
3863 priority = priority_c ? g_strdup (priority_c) : NULL;
3864 fwd_delay = fwd_delay_c ? g_strdup (fwd_delay_c) : NULL;
3865 hello_time = hello_time_c ? g_strdup (hello_time_c) : NULL;
3866 max_age = max_age_c ? g_strdup (max_age_c) : NULL;
3867 ageing_time = ageing_time_c ? g_strdup (ageing_time_c) : NULL;
3868 if (ask)
3869 do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time,
3870 &max_age, &ageing_time);
3871
3872 /* Use connection's ifname as 'bridge' ifname if exists, else generate one */
3873 ifname = nm_setting_connection_get_interface_name (s_con);
3874 if (!ifname)
3875 bridge_ifname = unique_master_iface_ifname (all_connections,
3876 NM_SETTING_BRIDGE_SETTING_NAME,
3877 NM_SETTING_BRIDGE_INTERFACE_NAME,
3878 "nm-bridge");
3879 else
3880 bridge_ifname = g_strdup (ifname);
3881
3882 if (stp) {
3883 GError *tmp_err = NULL;
3884 if (!nmc_string_to_bool (stp, &stp_bool, &tmp_err)) {
3885 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3886 _("Error: 'stp': %s."), tmp_err->message);
3887 g_clear_error (&tmp_err);
3888 goto cleanup_bridge;
3889 }
3890 }
3891
3892 /* Add 'bond' setting */
3893 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
3894 s_bridge = (NMSettingBridge *) nm_setting_bridge_new ();
3895 nm_connection_add_setting (connection, NM_SETTING (s_bridge));
3896
3897 if (priority)
3898 if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE,
3899 NM_SETTING_BRIDGE_PRIORITY, &stp_prio_int, error))
3900 goto cleanup_bridge;
3901 if (fwd_delay)
3902 if (!bridge_prop_string_to_uint (fwd_delay, "forward-delay", NM_TYPE_SETTING_BRIDGE,
3903 NM_SETTING_BRIDGE_FORWARD_DELAY, &fwd_delay_int, error))
3904 goto cleanup_bridge;
3905 if (hello_time)
3906 if (!bridge_prop_string_to_uint (hello_time, "hello-time", NM_TYPE_SETTING_BRIDGE,
3907 NM_SETTING_BRIDGE_HELLO_TIME, &hello_time_int, error))
3908 goto cleanup_bridge;
3909 if (max_age)
3910 if (!bridge_prop_string_to_uint (max_age, "max-age", NM_TYPE_SETTING_BRIDGE,
3911 NM_SETTING_BRIDGE_MAX_AGE, &max_age_int, error))
3912 goto cleanup_bridge;
3913 if (ageing_time)
3914 if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE,
3915 NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error))
3916 goto cleanup_bridge;
3917
3918 /* Set bridge options */
3919 g_object_set (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, bridge_ifname, NULL);
3920 if (stp)
3921 g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, stp_bool, NULL);
3922 if (priority)
3923 g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, stp_prio_int, NULL);
3924 if (fwd_delay)
3925 g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, fwd_delay_int, NULL);
3926 if (hello_time)
3927 g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, hello_time_int, NULL);
3928 if (max_age)
3929 g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL);
3930 if (ageing_time)
3931 g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL);
3932
3933 success = TRUE;
3934 cleanup_bridge:
3935 g_free (bridge_ifname);
3936 g_free (stp);
3937 g_free (priority);
3938 g_free (fwd_delay);
3939 g_free (hello_time);
3940 g_free (max_age);
3941 g_free (ageing_time);
3942 if (!success)
3943 return FALSE;
3944
3945 } else if (!strcmp (con_type, "bridge-slave")) {
3946 /* Build up the settings required for 'bridge-slave' */
3947 gboolean success = FALSE;
3948 const char *master = NULL;
3949 char *master_ask = NULL;
3950 const char *type = NULL;
3951 const char *priority_c = NULL;
3952 char *priority = NULL;
3953 const char *path_cost_c = NULL;
3954 char *path_cost = NULL;
3955 const char *hairpin_c = NULL;
3956 char *hairpin = NULL;
3957 unsigned long prio_int, path_cost_int;
3958 gboolean hairpin_bool;
3959 nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask},
3960 {"type", TRUE, &type, FALSE},
3961 {"priority", TRUE, &priority_c, FALSE},
3962 {"path-cost", TRUE, &path_cost_c, FALSE},
3963 {"hairpin", TRUE, &hairpin_c, FALSE},
3964 {NULL} };
3965
3966 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error))
3967 return FALSE;
3968
3969 if (!master && ask)
3970 master = master_ask = nmc_get_user_input (_("Bridge master: "));
3971 if (!master) {
3972 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3973 _("Error: 'master' is required."));
3974 return FALSE;
3975 }
3976 if (!nm_utils_is_uuid (master) && !nm_utils_iface_valid_name (master)) {
3977 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3978 _("Error: 'master': '%s' is not valid UUID nor interface."),
3979 master);
3980 goto cleanup_bridge_slave;
3981 }
3982
3983 if (type)
3984 printf (_("Warning: 'type' is currently ignored. "
3985 "We only support ethernet slaves for now.\n"));
3986
3987 /* Add 'bridge-port' setting */
3988 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
3989 s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new ();
3990 nm_connection_add_setting (connection, NM_SETTING (s_bridge_port));
3991
3992 /* Also ask for all optional arguments if '--ask' is specified. */
3993 priority = priority_c ? g_strdup (priority_c) : NULL;
3994 path_cost = path_cost_c ? g_strdup (path_cost_c) : NULL;
3995 hairpin = hairpin_c ? g_strdup (hairpin_c) : NULL;
3996 if (ask)
3997 do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin);
3998
3999 if (priority)
4000 if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
4001 NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error))
4002 goto cleanup_bridge_slave;
4003 if (path_cost)
4004 if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
4005 NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error))
4006 goto cleanup_bridge_slave;
4007 if (hairpin) {
4008 GError *tmp_err = NULL;
4009 if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) {
4010 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4011 _("Error: 'hairpin': %s."), tmp_err->message);
4012 g_clear_error (&tmp_err);
4013 goto cleanup_bridge_slave;
4014 }
4015 }
4016
4017 /* Change properties in 'connection' setting */
4018 g_object_set (s_con,
4019 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
4020 NM_SETTING_CONNECTION_MASTER, master,
4021 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME,
4022 NULL);
4023
4024 /* Add ethernet setting */
4025 s_wired = (NMSettingWired *) nm_setting_wired_new ();
4026 nm_connection_add_setting (connection, NM_SETTING (s_wired));
4027
4028 if (priority)
4029 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL);
4030 if (path_cost)
4031 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL);
4032 if (hairpin)
4033 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL);
4034
4035 success = TRUE;
4036 cleanup_bridge_slave:
4037 g_free (master_ask);
4038 g_free (priority);
4039 g_free (path_cost);
4040 g_free (hairpin);
4041 if (!success)
4042 return FALSE;
4043
4044 } else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) {
4045 /* Build up the settings required for 'vpn' */
4046 gboolean success = FALSE;
4047 const char *valid_vpns[] = { "openvpn", "vpnc", "pptp", "openconnect", "openswan", NULL };
4048 const char *vpn_type = NULL;
4049 char *vpn_type_ask = NULL;
4050 const char *user_c = NULL;
4051 char *user = NULL;
4052 const char *st;
4053 char *service_type = NULL;
4054 GError *tmp_err = NULL;
4055 nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask},
4056 {"user", TRUE, &user_c, FALSE},
4057 {NULL} };
4058
4059 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4060 return FALSE;
4061
4062 if (!vpn_type && ask)
4063 vpn_type = vpn_type_ask = nmc_get_user_input (_("VPN type: "));
4064 if (!vpn_type) {
4065 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4066 _("Error: 'vpn-type' is required."));
4067 goto cleanup_vpn;
4068 }
4069
4070 /* Also ask for all optional arguments if '--ask' is specified. */
4071 user = user_c ? g_strdup (user_c) : NULL;
4072 if (ask)
4073 do_questionnaire_vpn (&user);
4074
4075 if (!(st = nmc_string_is_valid (vpn_type, valid_vpns, &tmp_err))) {
4076 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4077 _("Error: 'vpn-type': %s."), tmp_err->message);
4078 g_clear_error (&tmp_err);
4079 goto cleanup_vpn;
4080 }
4081 service_type = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, st);
4082
4083 /* Add 'vpn' setting */
4084 s_vpn = (NMSettingVPN *) nm_setting_vpn_new ();
4085 nm_connection_add_setting (connection, NM_SETTING (s_vpn));
4086
4087 g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL);
4088 g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL);
4089
4090 success = TRUE;
4091 cleanup_vpn:
4092 g_free (vpn_type_ask);
4093 g_free (service_type);
4094 g_free (user);
4095 if (!success)
4096 return FALSE;
4097
4098 } else if (!strcmp (con_type, NM_SETTING_OLPC_MESH_SETTING_NAME)) {
4099 /* Build up the settings required for 'olpc' */
4100 gboolean success = FALSE;
4101 char *ssid_ask = NULL;
4102 const char *ssid = NULL;
4103 GByteArray *ssid_arr;
4104 const char *channel_c = NULL;
4105 char *channel = NULL;
4106 unsigned long chan;
4107 const char *dhcp_anycast_c = NULL;
4108 char *dhcp_anycast = NULL;
4109 GByteArray *array = NULL;
4110 nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask},
4111 {"channel", TRUE, &channel_c, FALSE},
4112 {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE},
4113 {NULL} };
4114
4115 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4116 return FALSE;
4117
4118 if (!ssid && ask)
4119 ssid = ssid_ask = nmc_get_user_input (_("SSID: "));
4120 if (!ssid) {
4121 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4122 _("Error: 'ssid' is required."));
4123 goto cleanup_olpc;
4124 }
4125
4126 /* Also ask for all optional arguments if '--ask' is specified. */
4127 channel = channel_c ? g_strdup (channel_c) : NULL;
4128 dhcp_anycast = dhcp_anycast_c ? g_strdup (dhcp_anycast_c) : NULL;
4129 if (ask)
4130 do_questionnaire_olpc (&channel, &dhcp_anycast);
4131
4132 if (channel) {
4133 if (!nmc_string_to_uint (channel, TRUE, 1, 13, &chan)) {
4134 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4135 _("Error: 'channel': '%s' is not valid; use <1-13>."),
4136 channel);
4137 goto cleanup_olpc;
4138 }
4139 }
4140 if (!check_and_convert_mac (dhcp_anycast, &array, ARPHRD_ETHER, "dhcp-anycast", error))
4141 goto cleanup_olpc;
4142
4143 /* Add OLPC mesh setting */
4144 s_olpc_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new ();
4145 nm_connection_add_setting (connection, NM_SETTING (s_olpc_mesh));
4146
4147 ssid_arr = g_byte_array_sized_new (strlen (ssid));
4148 g_byte_array_append (ssid_arr, (const guint8 *) ssid, strlen (ssid));
4149 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_SSID, ssid_arr, NULL);
4150 if (channel)
4151 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, chan, NULL);
4152 else
4153 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, 1, NULL);
4154 if (array) {
4155 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, array, NULL);
4156 g_byte_array_free (array, TRUE);
4157 }
4158 g_byte_array_free (ssid_arr, TRUE);
4159
4160 success = TRUE;
4161 cleanup_olpc:
4162 g_free (ssid_ask);
4163 g_free (channel);
4164 g_free (dhcp_anycast);
4165 if (!success)
4166 return FALSE;
4167
4168 } else {
4169 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4170 _("Error: '%s' is not a valid connection type."),
4171 con_type);
4172 return FALSE;
4173 }
4174
4175 /* Read and add IP configuration */
4176 if ( strcmp (con_type, "bond-slave") != 0
4177 && strcmp (con_type, "team-slave") != 0
4178 && strcmp (con_type, "bridge-slave") != 0) {
4179
4180 NMIP4Address *ip4addr = NULL;
4181 NMIP6Address *ip6addr = NULL;
4182 const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL;
4183 nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE},
4184 {"ip6", TRUE, &ip6, FALSE}, {"gw6", TRUE, &gw6, FALSE},
4185 {NULL} };
4186
4187 while (argc) {
4188 nmc_arg_t *p;
4189
4190 /* reset 'found' flag */
4191 for (p = exp_args; p->name; p++)
4192 p->found = FALSE;
4193
4194 ip4 = gw4 = ip6 = gw6 = NULL;
4195
4196 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error))
4197 return FALSE;
4198
4199 if (ip4) {
4200 ip4addr = nmc_parse_and_build_ip4_address (ip4, gw4, error);
4201 if (!ip4addr) {
4202 g_prefix_error (error, _("Error: "));
4203 return FALSE;
4204 }
4205 }
4206 add_ip4_address_to_connection (ip4addr, connection);
4207
4208 if (ip6) {
4209 ip6addr = nmc_parse_and_build_ip6_address (ip6, gw6, error);
4210 if (!ip6addr) {
4211 g_prefix_error (error, _("Error: "));
4212 return FALSE;
4213 }
4214 }
4215 add_ip6_address_to_connection (ip6addr, connection);
4216 }
4217
4218 /* Ask for addresses if '--ask' is specified. */
4219 if (ask)
4220 do_questionnaire_ip (connection);
4221 }
4222
4223 return TRUE;
4224 }
4225
4226 static char *
4227 unique_connection_name (GSList *list, const char *try_name)
4228 {
4229 NMConnection *connection;
4230 const char *name;
4231 char *new_name;
4232 unsigned int num = 1;
4233 GSList *iterator = list;
4234
4235 new_name = g_strdup (try_name);
4236 while (iterator) {
4237 connection = NM_CONNECTION (iterator->data);
4238
4239 name = nm_connection_get_id (connection);
4240 if (g_strcmp0 (new_name, name) == 0) {
4241 g_free (new_name);
4242 new_name = g_strdup_printf ("%s-%d", try_name, num++);
4243 iterator = list;
4244 }
4245 iterator = g_slist_next (iterator);
4246 }
4247 return new_name;
4248 }
4249
4250 typedef struct {
4251 NmCli *nmc;
4252 char *con_name;
4253 } AddConnectionInfo;
4254
4255 static void
4256 add_connection_cb (NMRemoteSettings *settings,
4257 NMRemoteConnection *connection,
4258 GError *error,
4259 gpointer user_data)
4260 {
4261 AddConnectionInfo *info = (AddConnectionInfo *) user_data;
4262 NmCli *nmc = info->nmc;
4263
4264 if (error) {
4265 g_string_printf (nmc->return_text,
4266 _("Error: Failed to add '%s' connection: (%d) %s"),
4267 info->con_name, error->code, error->message);
4268 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
4269 } else {
4270 printf (_("Connection '%s' (%s) successfully added.\n"),
4271 nm_connection_get_id (NM_CONNECTION (connection)),
4272 nm_connection_get_uuid (NM_CONNECTION (connection)));
4273 }
4274
4275 g_free (info->con_name);
4276 g_free (info);
4277 quit ();
4278 }
4279
4280 static NMCResultCode
4281 do_connection_add (NmCli *nmc, int argc, char **argv)
4282 {
4283 NMConnection *connection = NULL;
4284 NMSettingConnection *s_con;
4285 char *uuid;
4286 char *default_name = NULL;
4287 const char *type = NULL;
4288 char *type_ask = NULL;
4289 const char *con_name = NULL;
4290 const char *autoconnect = NULL;
4291 gboolean auto_bool = TRUE;
4292 const char *ifname = NULL;
4293 char *ifname_ask = NULL;
4294 gboolean ifname_mandatory = TRUE;
4295 AddConnectionInfo *info = NULL;
4296 const char *setting_name;
4297 GError *error = NULL;
4298 nmc_arg_t exp_args[] = { {"type", TRUE, &type, !nmc->ask},
4299 {"con-name", TRUE, &con_name, FALSE},
4300 {"autoconnect", TRUE, &autoconnect, FALSE},
4301 {"ifname", TRUE, &ifname, FALSE},
4302 {NULL} };
4303
4304 nmc->return_value = NMC_RESULT_SUCCESS;
4305
4306 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) {
4307 g_string_assign (nmc->return_text, error->message);
4308 nmc->return_value = error->code;
4309 g_clear_error (&error);
4310 goto error;
4311 }
4312
4313 if (!type && nmc->ask) {
4314 char *types_tmp = get_valid_options_string (nmc_valid_connection_types);
4315 printf ("Valid types: [%s]\n", types_tmp);
4316 type = type_ask = nmc_get_user_input (_("Connection type: "));
4317 g_free (types_tmp);
4318 }
4319 if (!type) {
4320 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
4321 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
4322 goto error;
4323 }
4324
4325 if (!(setting_name = check_valid_name (type, nmc_valid_connection_types, &error))) {
4326 g_string_printf (nmc->return_text, _("Error: invalid connection type; %s."),
4327 error->message);
4328 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
4329 g_clear_error (&error);
4330 goto error;
4331 }
4332 if (autoconnect) {
4333 GError *tmp_err = NULL;
4334 if (!nmc_string_to_bool (autoconnect, &auto_bool, &tmp_err)) {
4335 g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."),
4336 tmp_err->message);
4337 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
4338 g_clear_error (&tmp_err);
4339 goto error;
4340 }
4341 }
4342
4343 /* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */
4344 if ( strcmp (type, NM_SETTING_BOND_SETTING_NAME) == 0
4345 || strcmp (type, NM_SETTING_TEAM_SETTING_NAME) == 0
4346 || strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME) == 0
4347 || strcmp (type, NM_SETTING_VLAN_SETTING_NAME) == 0)
4348 ifname_mandatory = FALSE;
4349
4350 if (!ifname && ifname_mandatory && nmc->ask)
4351 ifname = ifname_ask = nmc_get_user_input (_("Interface name [*]: "));
4352 if (!ifname && ifname_mandatory)
4353 ifname = ifname_ask = g_strdup ("*");
4354
4355 if (ifname) {
4356 if (!nm_utils_iface_valid_name (ifname) && strcmp (ifname, "*") != 0) {
4357 g_string_printf (nmc->return_text,
4358 _("Error: 'ifname': '%s' is not a valid interface nor '*'."),
4359 ifname);
4360 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
4361 goto error;
4362 }
4363 /* Special value of '*' means no specific interface name */
4364 if (strcmp (ifname, "*") == 0)
4365 ifname = NULL;
4366 }
4367
4368 /* Create a new connection object */
4369 connection = nm_connection_new ();
4370
4371 /* Build up the 'connection' setting */
4372 s_con = (NMSettingConnection *) nm_setting_connection_new ();
4373 uuid = nm_utils_uuid_generate ();
4374 if (con_name)
4375 default_name = g_strdup (con_name);
4376 else {
4377 char *try_name = ifname ?
4378 g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname)
4379 : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types));
4380 default_name = unique_connection_name (nmc->system_connections, try_name);
4381 g_free (try_name);
4382 }
4383 g_object_set (s_con,
4384 NM_SETTING_CONNECTION_ID, default_name,
4385 NM_SETTING_CONNECTION_UUID, uuid,
4386 NM_SETTING_CONNECTION_TYPE, setting_name,
4387 NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool,
4388 NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
4389 NULL);
4390 g_free (uuid);
4391 g_free (default_name);
4392 nm_connection_add_setting (connection, NM_SETTING (s_con));
4393
4394 if (!complete_connection_by_type (connection,
4395 setting_name,
4396 nmc->system_connections,
4397 nmc->ask,
4398 argc,
4399 argv,
4400 &error)) {
4401 g_string_assign (nmc->return_text, error->message);
4402 nmc->return_value = error->code;
4403 g_clear_error (&error);
4404 goto error;
4405 }
4406
4407 nmc->should_wait = TRUE;
4408
4409 info = g_malloc0 (sizeof (AddConnectionInfo));
4410 info->nmc = nmc;
4411 info->con_name = g_strdup (nm_connection_get_id (connection));
4412
4413 /* Tell the settings service to add the new connection */
4414 nm_remote_settings_add_connection (nmc->system_settings,
4415 connection,
4416 add_connection_cb,
4417 info);
4418
4419 if (connection)
4420 g_object_unref (connection);
4421
4422 return nmc->return_value;
4423
4424 error:
4425 if (connection)
4426 g_object_unref (connection);
4427 g_free (type_ask);
4428 g_free (ifname_ask);
4429
4430 nmc->should_wait = FALSE;
4431 return nmc->return_value;
4432 }
4433
4434
4435 /*----------------------------------------------------------------------------*/
4436
4437 typedef char *CPFunction ();
4438 typedef char **CPPFunction ();
4439 /* History entry struct copied from libreadline's history.h */
4440 typedef struct _hist_entry {
4441 char *line;
4442 char *timestamp;
4443 char *data;
4444 } HIST_ENTRY;
4445
4446 typedef char * (*ReadLineFunc) (const char *);
4447 typedef void (*AddHistoryFunc) (const char *);
4448 typedef HIST_ENTRY** (*HistoryListFunc) (void);
4449 typedef int (*RlInsertTextFunc) (char *);
4450 typedef char ** (*RlCompletionMatchesFunc) (char *, CPFunction *);
4451
4452 typedef struct {
4453 ReadLineFunc readline_func;
4454 AddHistoryFunc add_history_func;
4455 HistoryListFunc history_list_func;
4456 RlInsertTextFunc rl_insert_text_func;
4457 void **rl_startup_hook_x;
4458 RlCompletionMatchesFunc completion_matches_func;
4459 void **rl_attempted_completion_function_x;
4460 void **rl_completion_entry_function_x;
4461 char **rl_line_buffer_x;
4462 char **rl_prompt_x;
4463 int *rl_attempted_completion_over_x;
4464 int *rl_completion_append_character_x;
4465 const char **rl_completer_word_break_characters_x;
4466 void (*rl_free_line_state_func) (void);
4467 void (*rl_cleanup_after_signal_func) (void);
4468 } EditLibSymbols;
4469
4470 static EditLibSymbols edit_lib_symbols;
4471 static char *pre_input_deftext;
4472
4473 static int
4474 set_deftext (void)
4475 {
4476 if ( pre_input_deftext
4477 && edit_lib_symbols.rl_insert_text_func
4478 && edit_lib_symbols.rl_startup_hook_x) {
4479 edit_lib_symbols.rl_insert_text_func (pre_input_deftext);
4480 g_free (pre_input_deftext);
4481 pre_input_deftext = NULL;
4482 *edit_lib_symbols.rl_startup_hook_x = NULL;
4483 }
4484 return 0;
4485 }
4486
4487 static char *
4488 gen_func_basic (char *text, int state, const char **words)
4489 {
4490 static int list_idx, len;
4491 const char *name;
4492
4493 if (!state) {
4494 list_idx = 0;
4495 len = strlen (text);
4496 }
4497
4498 /* Return the next name which partially matches one from the 'words' list. */
4499 while ((name = words[list_idx])) {
4500 list_idx++;
4501
4502 if (strncmp (name, text, len) == 0)
4503 return g_strdup (name);
4504 }
4505 return NULL;
4506 }
4507
4508 static char *
4509 gen_nmcli_cmds_menu (char *text, int state)
4510 {
4511 const char *words[] = { "goto", "set", "remove", "describe", "print", "verify",
4512 "save", "back", "help", "quit", "nmcli",
4513 NULL };
4514 return gen_func_basic (text, state, words);
4515 }
4516
4517 static char *
4518 gen_nmcli_cmds_submenu (char *text, int state)
4519 {
4520 const char *words[] = { "set", "add", "change", "remove", "describe",
4521 "print", "back", "help", "quit",
4522 NULL };
4523 return gen_func_basic (text, state, words);
4524 }
4525
4526 static char *
4527 gen_cmd_nmcli (char *text, int state)
4528 {
4529 const char *words[] = { "status-line", "save-confirmation", "prompt-color", NULL };
4530 return gen_func_basic (text, state, words);
4531 }
4532
4533 static char *
4534 gen_cmd_nmcli_prompt_color (char *text, int state)
4535 {
4536 const char *words[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", NULL };
4537 return gen_func_basic (text, state, words);
4538 }
4539
4540 static char *
4541 gen_func_bool_values (char *text, int state)
4542 {
4543 const char *words[] = { "yes", "no", NULL };
4544 return gen_func_basic (text, state, words);
4545 }
4546
4547 static char *
4548 gen_cmd_verify0 (char *text, int state)
4549 {
4550 const char *words[] = { "all", NULL };
4551 return gen_func_basic (text, state, words);
4552 }
4553
4554 static char *
4555 gen_cmd_print2 (char *text, int state)
4556 {
4557 const char *words[] = { "setting", "connection", "all", NULL };
4558 return gen_func_basic (text, state, words);
4559 }
4560
4561 static char *
4562 gen_connection_types (char *text, int state)
4563 {
4564 static int list_idx, len;
4565 const char *c_type, *a_type;
4566
4567 if (!state) {
4568 list_idx = 0;
4569 len = strlen (text);
4570 }
4571
4572 while (nmc_valid_connection_types[list_idx].name) {
4573 a_type = nmc_valid_connection_types[list_idx].alias;
4574 c_type = nmc_valid_connection_types[list_idx].name;
4575 list_idx++;
4576 if (a_type && !strncmp (text, a_type, len))
4577 return g_strdup (a_type);
4578 if (c_type && !strncmp (text, c_type, len))
4579 return g_strdup (c_type);
4580 }
4581
4582 return NULL;
4583 }
4584
4585 static char *
4586 gen_setting_names (char *text, int state)
4587 {
4588 static int list_idx, len;
4589 const char *s_name, *a_name;
4590 const NameItem *valid_settings_arr;
4591
4592 if (!state) {
4593 list_idx = 0;
4594 len = strlen (text);
4595 }
4596
4597 valid_settings_arr = get_valid_settings_array (nmc_completion_con_type);
4598 if (!valid_settings_arr)
4599 return NULL;
4600 while (valid_settings_arr[list_idx].name) {
4601 a_name = valid_settings_arr[list_idx].alias;
4602 s_name = valid_settings_arr[list_idx].name;
4603 list_idx++;
4604 if (len == 0 && a_name)
4605 return g_strdup_printf ("%s (%s)", s_name, a_name);
4606 if (a_name && !strncmp (text, a_name, len))
4607 return g_strdup (a_name);
4608 if (s_name && !strncmp (text, s_name, len))
4609 return g_strdup (s_name);
4610 }
4611 return NULL;
4612 }
4613
4614 static char *
4615 gen_property_names (char *text, int state)
4616 {
4617 NMSetting *setting = NULL;
4618 char **valid_props = NULL;
4619 char *ret = NULL;
4620 char *line = g_strdup (*edit_lib_symbols.rl_line_buffer_x);
4621 const char *setting_name;
4622 char **strv = NULL;
4623 const NameItem *valid_settings_arr;
4624 const char *p1;
4625
4626 /* Try to get the setting from 'line' - setting_name.property */
4627 p1 = strchr (line, '.');
4628 if (p1) {
4629 while (p1 > line && !g_ascii_isspace (*p1))
4630 p1--;
4631
4632 strv = g_strsplit (p1+1, ".", 2);
4633
4634 valid_settings_arr = get_valid_settings_array (nmc_completion_con_type);
4635 setting_name = check_valid_name (strv[0], valid_settings_arr, NULL);
4636 setting = nmc_setting_new_for_name (setting_name);
4637 } else {
4638 /* Else take the current setting, if any */
4639 setting = nmc_completion_setting ? g_object_ref (nmc_completion_setting) : NULL;
4640 }
4641
4642 if (setting) {
4643 valid_props = nmc_setting_get_valid_properties (setting);
4644 ret = gen_func_basic (text, state, (const char **) valid_props);
4645 }
4646
4647 g_free (line);
4648 g_strfreev (strv);
4649 g_strfreev (valid_props);
4650 if (setting)
4651 g_object_unref (setting);
4652 return ret;
4653 }
4654
4655 typedef char * (*my_gen_func_ptr) (char *, int);
4656 static my_gen_func_ptr
4657 get_gen_func_cmd_nmcli (char *str)
4658 {
4659 if (!str)
4660 return NULL;
4661 if (matches (str, "status-line") == 0)
4662 return gen_func_bool_values;
4663 if (matches (str, "save-confirmation") == 0)
4664 return gen_func_bool_values;
4665 if (matches (str, "prompt-color") == 0)
4666 return gen_cmd_nmcli_prompt_color;
4667 return NULL;
4668 }
4669
4670 /*
4671 * Helper function parsing line for completion.
4672 * IN:
4673 * line : the whole line to be parsed
4674 * end : the position of cursor in the line
4675 * cmd : command to match
4676 * OUT:
4677 * cw_num : is set to the word number being completed (1, 2, 3, 4).
4678 * prev_word : returns the previous word (so that we have some context).
4679 *
4680 * Returns TRUE when the first word of the 'line' matches 'cmd'.
4681 *
4682 * Examples:
4683 * line="rem" cmd="remove" -> TRUE cw_num=1
4684 * line="set con" cmd="set" -> TRUE cw_num=2
4685 * line="go ipv4.method" cmd="goto" -> TRUE cw_num=2
4686 * line=" des eth.mtu " cmd="describe" -> TRUE cw_num=3
4687 * line=" bla ipv4.method" cmd="goto" -> FALSE
4688 */
4689 static gboolean
4690 should_complete_cmd (const char *line, int end, const char *cmd,
4691 int *cw_num, char **prev_word)
4692 {
4693 char *tmp;
4694 const char *word1, *word2, *word3;
4695 size_t n1, n2, n3, n4, n5, n6;
4696 gboolean word1_done, word2_done, word3_done;
4697 gboolean ret = FALSE;
4698
4699 if (!line)
4700 return FALSE;
4701
4702 tmp = g_strdup (line);
4703
4704 n1 = strspn (tmp, " \t");
4705 n2 = strcspn (tmp+n1, " \t\0") + n1;
4706 n3 = strspn (tmp+n2, " \t") + n2;
4707 n4 = strcspn (tmp+n3, " \t\0") + n3;
4708 n5 = strspn (tmp+n4, " \t") + n4;
4709 n6 = strcspn (tmp+n5, " \t\0") + n5;
4710
4711 word1_done = end > n2;
4712 word2_done = end > n4;
4713 word3_done = end > n6;
4714 tmp[n2] = tmp[n4] = tmp[n6] = '\0';
4715
4716 word1 = tmp[n1] ? tmp + n1 : NULL;
4717 word2 = tmp[n3] ? tmp + n3 : NULL;
4718 word3 = tmp[n5] ? tmp + n5 : NULL;
4719
4720 if (!word1_done) {
4721 if (cw_num)
4722 *cw_num = 1;
4723 if (prev_word)
4724 *prev_word = NULL;
4725 } else if (!word2_done) {
4726 if (cw_num)
4727 *cw_num = 2;
4728 if (prev_word)
4729 *prev_word = g_strdup (word1);
4730 } else if (!word3_done) {
4731 if (cw_num)
4732 *cw_num = 3;
4733 if (prev_word)
4734 *prev_word = g_strdup (word2);
4735 } else {
4736 if (cw_num)
4737 *cw_num = 4;
4738 if (prev_word)
4739 *prev_word = g_strdup (word3);
4740 }
4741
4742 if (word1 && matches (word1, cmd) == 0)
4743 ret = TRUE;
4744
4745 g_free (tmp);
4746 return ret;
4747 }
4748
4749 /*
4750 * Attempt to complete on the contents of TEXT. START and END show the
4751 * region of TEXT that contains the word to complete. We can use the
4752 * entire line in case we want to do some simple parsing. Return the
4753 * array of matches, or NULL if there aren't any.
4754 */
4755 static char **
4756 nmcli_editor_tab_completion (char *text, int start, int end)
4757 {
4758 char **match_array = NULL;
4759 const char *line = *edit_lib_symbols.rl_line_buffer_x;
4760 const char *prompt = *edit_lib_symbols.rl_prompt_x;
4761 CPFunction *generator_func = NULL;
4762 gboolean copy_char;
4763 const char *p1;
4764 char *p2, *prompt_tmp;
4765 char *word = NULL;
4766 size_t n1;
4767 int num;
4768
4769 /* Restore standard append character to space */
4770 *edit_lib_symbols.rl_completion_append_character_x = ' ';
4771
4772 /* Filter out possible ANSI color escape sequences */
4773 p1 = prompt;
4774 p2 = prompt_tmp = g_strdup (prompt);
4775 copy_char = TRUE;
4776 while (*p1) {
4777 if (*p1 == '\33')
4778 copy_char = FALSE;
4779 if (copy_char)
4780 *p2++ = *p1;
4781 if (!copy_char && *p1 == 'm')
4782 copy_char = TRUE;
4783 p1++;
4784 }
4785 *p2 = '\0';
4786
4787 /* Find the first non-space character */
4788 n1 = strspn (line, " \t");
4789
4790 /* Choose the right generator function */
4791 if (strcmp (prompt_tmp, EDITOR_PROMPT_CON_TYPE) == 0)
4792 generator_func = gen_connection_types;
4793 else if (strcmp (prompt_tmp, EDITOR_PROMPT_SETTING) == 0)
4794 generator_func = gen_setting_names;
4795 else if (strcmp (prompt_tmp, EDITOR_PROMPT_PROPERTY) == 0)
4796 generator_func = gen_property_names;
4797 else if (g_str_has_prefix (prompt_tmp, "nmcli")) {
4798 if (!strchr (prompt_tmp, '.')) {
4799 int level = g_str_has_prefix (prompt_tmp, "nmcli>") ? 0 : 1;
4800 const char *dot = strchr (line, '.');
4801
4802 /* Main menu - level 0,1 */
4803 if (start == n1)
4804 generator_func = gen_nmcli_cmds_menu;
4805 else {
4806 if (should_complete_cmd (line, end, "goto", &num, NULL) && num <= 2) {
4807 if (level == 0 && (!dot || dot >= line + end))
4808 generator_func = gen_setting_names;
4809 else
4810 generator_func = gen_property_names;
4811 } else if ( ( should_complete_cmd (line, end, "set", &num, NULL)
4812 || should_complete_cmd (line, end, "remove", &num, NULL)
4813 || should_complete_cmd (line, end, "describe", &num, NULL))
4814 && num <= 2) {
4815 if (level == 0 && (!dot || dot >= line + end)) {
4816 generator_func = gen_setting_names;
4817 *edit_lib_symbols.rl_completion_append_character_x = '.';
4818 } else
4819 generator_func = gen_property_names;
4820 } else if (should_complete_cmd (line, end, "nmcli", &num, &word)) {
4821 if (num < 3)
4822 generator_func = gen_cmd_nmcli;
4823 else if (num == 3)
4824 generator_func = get_gen_func_cmd_nmcli (word);
4825 } else if ( should_complete_cmd (line, end, "print", &num, NULL)
4826 || should_complete_cmd (line, end, "verify", &num, NULL)) {
4827 if (num <= 2)
4828 generator_func = gen_cmd_verify0;
4829 } else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
4830 generator_func = gen_nmcli_cmds_menu;
4831 }
4832 } else {
4833 /* Submenu - level 2 */
4834 if (start == n1)
4835 generator_func = gen_nmcli_cmds_submenu;
4836 else {
4837 if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2)
4838 generator_func = gen_cmd_print2;
4839 else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
4840 generator_func = gen_nmcli_cmds_submenu;
4841 }
4842 }
4843 }
4844
4845 if (generator_func)
4846 match_array = edit_lib_symbols.completion_matches_func (text, generator_func);
4847
4848 /* Disable default filename completion */
4849 if (!match_array)
4850 *edit_lib_symbols.rl_attempted_completion_over_x = 1;
4851
4852 g_free (prompt_tmp);
4853 g_free (word);
4854 return match_array;
4855 }
4856
4857 static GModule *
4858 load_cmd_line_edit_lib (void)
4859 {
4860 GModule *module;
4861 char *lib_path;
4862 int i;
4863 static const char * const edit_lib_table[] = {
4864 "libreadline.so.6", /* GNU Readline library version 6 - latest */
4865 "libreadline.so.5", /* GNU Readline library version 5 - previous */
4866 "libedit.so.0", /* NetBSD Editline library port (http://www.thrysoee.dk/editline/) */
4867 };
4868
4869 /* Try to load a library for line editing */
4870 for (i = 0; i < G_N_ELEMENTS (edit_lib_table); i++) {
4871 lib_path = g_module_build_path (NULL, edit_lib_table[i]);
4872 module = g_module_open (lib_path, G_MODULE_BIND_LOCAL);
4873 g_free (lib_path);
4874 if (module)
4875 break;
4876 }
4877 if (!module)
4878 return NULL;
4879
4880 if (!g_module_symbol (module, "readline", (gpointer) (&edit_lib_symbols.readline_func)))
4881 goto error;
4882 if (!g_module_symbol (module, "add_history", (gpointer) (&edit_lib_symbols.add_history_func)))
4883 goto error;
4884 if (!g_module_symbol (module, "history_list", (gpointer) (&edit_lib_symbols.history_list_func)))
4885 goto error;
4886 if (!g_module_symbol (module, "rl_insert_text", (gpointer) (&edit_lib_symbols.rl_insert_text_func)))
4887 goto error;
4888 if (!g_module_symbol (module, "rl_startup_hook", (gpointer) (&edit_lib_symbols.rl_startup_hook_x)))
4889 goto error;
4890 if (!g_module_symbol (module, "rl_attempted_completion_function",
4891 (gpointer) (&edit_lib_symbols.rl_attempted_completion_function_x)))
4892 goto error;
4893 if (!g_module_symbol (module, "completion_matches",
4894 (gpointer) (&edit_lib_symbols.completion_matches_func)))
4895 goto error;
4896 if (!g_module_symbol (module, "rl_line_buffer",
4897 (gpointer) (&edit_lib_symbols.rl_line_buffer_x)))
4898 goto error;
4899 if (!g_module_symbol (module, "rl_prompt",
4900 (gpointer) (&edit_lib_symbols.rl_prompt_x)))
4901 goto error;
4902 if (!g_module_symbol (module, "rl_attempted_completion_over",
4903 (gpointer) (&edit_lib_symbols.rl_attempted_completion_over_x)))
4904 goto error;
4905 if (!g_module_symbol (module, "rl_completion_append_character",
4906 (gpointer) (&edit_lib_symbols.rl_completion_append_character_x)))
4907 goto error;
4908 if (!g_module_symbol (module, "rl_completer_word_break_characters",
4909 (gpointer) (&edit_lib_symbols.rl_completer_word_break_characters_x)))
4910 goto error;
4911 if (!g_module_symbol (module, "rl_free_line_state",
4912 (gpointer) (&edit_lib_symbols.rl_free_line_state_func)))
4913 goto error;
4914 if (!g_module_symbol (module, "rl_cleanup_after_signal",
4915 (gpointer) (&edit_lib_symbols.rl_cleanup_after_signal_func)))
4916 goto error;
4917
4918 /* Set a pointer to an alternative function to create matches */
4919 *edit_lib_symbols.rl_attempted_completion_function_x = (CPPFunction *) nmcli_editor_tab_completion;
4920
4921 /* Use ' ' and '.' as word break characters */
4922 *edit_lib_symbols.rl_completer_word_break_characters_x = ". ";
4923
4924 return module;
4925 error:
4926 g_module_close (module);
4927 return NULL;
4928 }
4929
4930 void
4931 nmc_cleanup_readline (void)
4932 {
4933 if (edit_lib_symbols.rl_free_line_state_func)
4934 edit_lib_symbols.rl_free_line_state_func ();
4935 if (edit_lib_symbols.rl_cleanup_after_signal_func)
4936 edit_lib_symbols.rl_cleanup_after_signal_func ();
4937 }
4938
4939 static char *
4940 readline_x (const char *prompt)
4941 {
4942 char *str;
4943
4944 if (edit_lib_symbols.readline_func) {
4945 str = edit_lib_symbols.readline_func (prompt);
4946 /* Return NULL, not empty string */
4947 if (str && *str == '\0') {
4948 g_free (str);
4949 str = NULL;
4950 }
4951 } else
4952 str = nmc_get_user_input (prompt);
4953
4954 if (edit_lib_symbols.add_history_func && str && *str)
4955 edit_lib_symbols.add_history_func (str);
4956
4957 return str;
4958 }
4959
4960
4961 #define NMCLI_EDITOR_HISTORY ".nmcli-history"
4962
4963 static void
4964 load_history_cmds (const char *uuid)
4965 {
4966 GKeyFile *kf;
4967 char *filename;
4968 char **keys;
4969 char *line;
4970 size_t i;
4971 GError *err = NULL;
4972
4973 /* Nothing to do if readline library is not used */
4974 if (!edit_lib_symbols.add_history_func)
4975 return;
4976
4977 filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
4978 kf = g_key_file_new ();
4979 if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
4980 if (err->code == G_KEY_FILE_ERROR_PARSE)
4981 printf ("Warning: %s parse error: %s\n", filename, err->message);
4982 g_key_file_free (kf);
4983 g_free (filename);
4984 return;
4985 }
4986 keys = g_key_file_get_keys (kf, uuid, NULL, NULL);
4987 for (i = 0; keys && keys[i]; i++) {
4988 line = g_key_file_get_string (kf, uuid, keys[i], NULL);
4989 if (line && *line)
4990 edit_lib_symbols.add_history_func (line);
4991 g_free (line);
4992 }
4993 g_strfreev (keys);
4994 g_key_file_free (kf);
4995 g_free (filename);
4996 }
4997
4998 static void
4999 save_history_cmds (const char *uuid)
5000 {
5001 HIST_ENTRY **hist = NULL;
5002 GKeyFile *kf;
5003 char *filename;
5004 size_t i;
5005 char *key;
5006 char *data;
5007 gsize len = 0;
5008 GError *err = NULL;
5009
5010 if (edit_lib_symbols.history_list_func)
5011 hist = edit_lib_symbols.history_list_func();
5012
5013 if (hist) {
5014 filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
5015 kf = g_key_file_new ();
5016 if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
5017 if ( err->code != G_FILE_ERROR_NOENT
5018 && err->code != G_KEY_FILE_ERROR_NOT_FOUND) {
5019 printf ("Warning: %s parse error: %s\n", filename, err->message);
5020 g_key_file_free (kf);
5021 g_free (filename);
5022 g_clear_error (&err);
5023 return;
5024 }
5025 g_clear_error (&err);
5026 }
5027
5028 /* Remove previous history group and save new history entries */
5029 g_key_file_remove_group (kf, uuid, NULL);
5030 for (i = 0; hist[i]; i++)
5031 {
5032 key = g_strdup_printf ("%zd", i);
5033 g_key_file_set_string (kf, uuid, key, hist[i]->line);
5034 g_free (key);
5035 }
5036
5037 /* Write history to file */
5038 data = g_key_file_to_data (kf, &len, NULL);
5039 if (data) {
5040 g_file_set_contents (filename, data, len, NULL);
5041 g_free (data);
5042 }
5043 g_key_file_free (kf);
5044 g_free (filename);
5045 }
5046 }
5047
5048 /*----------------------------------------------------------------------------*/
5049
5050 static void
5051 editor_show_connection (NMConnection *connection, NmCli *nmc)
5052 {
5053 nmc->print_output = NMC_PRINT_PRETTY;
5054 nmc->multiline_output = TRUE;
5055 nmc->escape_values = 0;
5056
5057 /* Remove any previous data */
5058 nmc_empty_output_fields (nmc);
5059
5060 nmc_connection_detail (connection, nmc);
5061 }
5062
5063 static void
5064 editor_show_setting (NMSetting *setting, NmCli *nmc)
5065 {
5066 printf (_("['%s' setting values]\n"),
5067 nm_setting_get_name (setting));
5068
5069 nmc->multiline_output = TRUE;
5070 nmc->escape_values = 0;
5071
5072 /* Remove any previous data */
5073 nmc_empty_output_fields (nmc);
5074
5075 setting_details (setting, nmc);
5076 }
5077
5078 typedef enum {
5079 NMC_EDITOR_MAIN_CMD_UNKNOWN = 0,
5080 NMC_EDITOR_MAIN_CMD_GOTO,
5081 NMC_EDITOR_MAIN_CMD_REMOVE,
5082 NMC_EDITOR_MAIN_CMD_SET,
5083 NMC_EDITOR_MAIN_CMD_DESCRIBE,
5084 NMC_EDITOR_MAIN_CMD_PRINT,
5085 NMC_EDITOR_MAIN_CMD_VERIFY,
5086 NMC_EDITOR_MAIN_CMD_SAVE,
5087 NMC_EDITOR_MAIN_CMD_BACK,
5088 NMC_EDITOR_MAIN_CMD_HELP,
5089 NMC_EDITOR_MAIN_CMD_NMCLI,
5090 NMC_EDITOR_MAIN_CMD_QUIT,
5091 } NmcEditorMainCmd;
5092
5093 static NmcEditorMainCmd
5094 parse_editor_main_cmd (const char *cmd, char **cmd_arg)
5095 {
5096 NmcEditorMainCmd editor_cmd = NMC_EDITOR_MAIN_CMD_UNKNOWN;
5097 char **vec;
5098
5099 vec = nmc_strsplit_set (cmd, " \t", 2);
5100 if (g_strv_length (vec) < 1) {
5101 if (cmd_arg)
5102 *cmd_arg = NULL;
5103 return NMC_EDITOR_MAIN_CMD_UNKNOWN;
5104 }
5105
5106 if (matches (vec[0], "goto") == 0)
5107 editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO;
5108 else if (matches (vec[0], "remove") == 0)
5109 editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE;
5110 else if (matches (vec[0], "set") == 0)
5111 editor_cmd = NMC_EDITOR_MAIN_CMD_SET;
5112 else if (matches (vec[0], "describe") == 0)
5113 editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE;
5114 else if (matches (vec[0], "print") == 0)
5115 editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT;
5116 else if (matches (vec[0], "verify") == 0)
5117 editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY;
5118 else if (matches (vec[0], "save") == 0)
5119 editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE;
5120 else if (matches (vec[0], "back") == 0)
5121 editor_cmd = NMC_EDITOR_MAIN_CMD_BACK;
5122 else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
5123 editor_cmd = NMC_EDITOR_MAIN_CMD_HELP;
5124 else if (matches (vec[0], "quit") == 0)
5125 editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT;
5126 else if (matches (vec[0], "nmcli") == 0)
5127 editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI;
5128
5129 /* set pointer to command argument */
5130 if (cmd_arg)
5131 *cmd_arg = vec[1] ? g_strstrip (g_strdup (vec[1])) : NULL;
5132
5133 g_strfreev (vec);
5134 return editor_cmd;
5135 }
5136
5137 static void
5138 editor_main_usage (void)
5139 {
5140 printf ("------------------------------------------------------------------------------\n");
5141 /* TRANSLATORS: do not translate command names and keywords before ::
5142 * However, you should translate terms enclosed in <>.
5143 */
5144 printf (_("---[ Main menu ]---\n"
5145 "goto [<setting> | <prop>] :: go to a setting or property\n"
5146 "remove <setting>[.<prop>] | <prop> :: remove setting or reset property value\n"
5147 "set [<setting>.<prop> <value>] :: set property value\n"
5148 "describe [<setting>.<prop>] :: describe property\n"
5149 "print [all] :: print the connection\n"
5150 "verify [all] :: verify the connection\n"
5151 "save :: save the connection\n"
5152 "back :: go one level up (back)\n"
5153 "help/? [<command>] :: print this help\n"
5154 "nmcli <conf-option> <value> :: nmcli configuration\n"
5155 "quit :: exit nmcli\n"));
5156 printf ("------------------------------------------------------------------------------\n");
5157 }
5158
5159 static void
5160 editor_main_help (const char *command)
5161 {
5162 if (!command)
5163 editor_main_usage ();
5164 else {
5165 /* detailed command descriptions */
5166 NmcEditorMainCmd cmd = parse_editor_main_cmd (command, NULL);
5167
5168 switch (cmd) {
5169 case NMC_EDITOR_MAIN_CMD_GOTO:
5170 printf (_("goto <setting>[.<prop>] | <prop> :: enter setting/property for editing\n\n"
5171 "This command enters into a setting or property for editing it.\n\n"
5172 "Examples: nmcli> goto connection\n"
5173 " nmcli connection> goto secondaries\n"
5174 " nmcli> goto ipv4.addresses\n"));
5175 break;
5176 case NMC_EDITOR_MAIN_CMD_REMOVE:
5177 printf (_("remove <setting>[.<prop>] :: remove setting or reset property value\n\n"
5178 "This command removes an entire setting from the connection, or if a property\n"
5179 "is given, resets that property to the default value.\n\n"
5180 "Examples: nmcli> remove wifi-sec\n"
5181 " nmcli> remove eth.mtu\n"));
5182 break;
5183 case NMC_EDITOR_MAIN_CMD_SET:
5184 printf (_("set [<setting>.<prop> <value>] :: set property value\n\n"
5185 "This command sets property value.\n\n"
5186 "Example: nmcli> set con.id My connection\n"));
5187 break;
5188 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
5189 printf (_("describe [<setting>.<prop>] :: describe property\n\n"
5190 "Shows property description. You can consult nm-settings(5) "
5191 "manual page to see all NM settings and properties.\n"));
5192 break;
5193 case NMC_EDITOR_MAIN_CMD_PRINT:
5194 printf (_("print [all] :: print setting or connection values\n\n"
5195 "Shows current property or the whole connection.\n\n"
5196 "Example: nmcli ipv4> print all\n"));
5197 break;
5198 case NMC_EDITOR_MAIN_CMD_VERIFY:
5199 printf (_("verify [all] :: verify setting or connection validity\n\n"
5200 "Verifies whether the setting or connection is valid and can "
5201 "be saved later. It indicates invalid values on error.\n\n"
5202 "Examples: nmcli> verify\n"
5203 " nmcli bond> verify\n"));
5204 break;
5205 case NMC_EDITOR_MAIN_CMD_SAVE:
5206 printf (_("save :: save the connection\n\n"
5207 "Sends the connection to NetworkManager that will save it.\n"));
5208 break;
5209 case NMC_EDITOR_MAIN_CMD_BACK:
5210 printf (_("back :: go to upper menu level\n\n"));
5211 break;
5212 case NMC_EDITOR_MAIN_CMD_HELP:
5213 printf (_("help/? [<command>] :: help for the nmcli commands\n\n"));
5214 break;
5215 case NMC_EDITOR_MAIN_CMD_NMCLI:
5216 printf (_("nmcli [<conf-option> <value>] :: nmcli configuration\n\n"
5217 "Configures nmcli. The following options are available:\n"
5218 "status-line yes | no [default: no]\n"
5219 "save-confirmation yes | no [default: yes]\n"
5220 "prompt-color <0-8> [default: 0]\n"
5221 " 0 = normal\n"
5222 " 1 = \33[30mblack\33[0m\n"
5223 " 2 = \33[31mred\33[0m\n"
5224 " 3 = \33[32mgreen\33[0m\n"
5225 " 4 = \33[33myellow\33[0m\n"
5226 " 5 = \33[34mblue\33[0m\n"
5227 " 6 = \33[35mmagenta\33[0m\n"
5228 " 7 = \33[36mcyan\33[0m\n"
5229 " 8 = \33[37mwhite\33[0m\n"
5230 "\n"
5231 "Examples: nmcli> nmcli status-line yes\n"
5232 " nmcli> nmcli save-confirmation no\n"
5233 " nmcli> nmcli prompt-color 3\n"));
5234 break;
5235 case NMC_EDITOR_MAIN_CMD_QUIT:
5236 printf (_("quit :: exit nmcli\n\n"
5237 "This command exits nmcli. When the connection being edited "
5238 "is not saved, the user is asked to confirm the action.\n"));
5239 break;
5240 default:
5241 printf (_("Unknown command: '%s'\n"), command);
5242 break;
5243 }
5244 }
5245 }
5246
5247 typedef enum {
5248 NMC_EDITOR_SUB_CMD_UNKNOWN = 0,
5249 NMC_EDITOR_SUB_CMD_SET,
5250 NMC_EDITOR_SUB_CMD_ADD,
5251 NMC_EDITOR_SUB_CMD_CHANGE,
5252 NMC_EDITOR_SUB_CMD_REMOVE,
5253 NMC_EDITOR_SUB_CMD_DESCRIBE,
5254 NMC_EDITOR_SUB_CMD_PRINT,
5255 NMC_EDITOR_SUB_CMD_BACK,
5256 NMC_EDITOR_SUB_CMD_HELP,
5257 NMC_EDITOR_SUB_CMD_QUIT
5258 } NmcEditorSubCmd;
5259
5260 static NmcEditorSubCmd
5261 parse_editor_sub_cmd (const char *cmd, char **cmd_arg)
5262 {
5263 NmcEditorSubCmd editor_cmd = NMC_EDITOR_SUB_CMD_UNKNOWN;
5264 char **vec;
5265
5266 vec = nmc_strsplit_set (cmd, " \t", 2);
5267 if (g_strv_length (vec) < 1) {
5268 if (cmd_arg)
5269 *cmd_arg = NULL;
5270 return NMC_EDITOR_SUB_CMD_UNKNOWN;
5271 }
5272
5273 if (matches (vec[0], "set") == 0)
5274 editor_cmd = NMC_EDITOR_SUB_CMD_SET;
5275 else if (matches (vec[0], "add") == 0)
5276 editor_cmd = NMC_EDITOR_SUB_CMD_ADD;
5277 else if (matches (vec[0], "change") == 0)
5278 editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE;
5279 else if (matches (vec[0], "remove") == 0)
5280 editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE;
5281 else if (matches (vec[0], "describe") == 0)
5282 editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE;
5283 else if (matches (vec[0], "print") == 0)
5284 editor_cmd = NMC_EDITOR_SUB_CMD_PRINT;
5285 else if (matches (vec[0], "back") == 0)
5286 editor_cmd = NMC_EDITOR_SUB_CMD_BACK;
5287 else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
5288 editor_cmd = NMC_EDITOR_SUB_CMD_HELP;
5289 else if (matches (vec[0], "quit") == 0)
5290 editor_cmd = NMC_EDITOR_SUB_CMD_QUIT;
5291
5292 /* set pointer to command argument */
5293 if (cmd_arg)
5294 *cmd_arg = g_strdup (vec[1]);
5295
5296 g_strfreev (vec);
5297 return editor_cmd;
5298 }
5299
5300 static void
5301 editor_sub_help (void)
5302 {
5303 printf ("------------------------------------------------------------------------------\n");
5304 /* TRANSLATORS: do not translate command names and keywords before ::
5305 * However, you should translate terms enclosed in <>.
5306 */
5307 printf (_("---[ Property menu ]---\n"
5308 "set [<value>] :: set new value\n"
5309 "add [<value>] :: add new option to the property\n"
5310 "change :: change current value\n"
5311 "remove [<index> | <option>] :: delete the value\n"
5312 "describe :: describe property\n"
5313 "print [setting | connection] :: print property (setting/connection) value(s)\n"
5314 "back :: go to upper level\n"
5315 "help/? [<command>] :: print this help or command description\n"
5316 "quit :: exit nmcli\n"));
5317 printf ("------------------------------------------------------------------------------\n");
5318 }
5319
5320 static void
5321 editor_sub_usage (const char *command)
5322 {
5323
5324 if (!command)
5325 editor_sub_help ();
5326 else {
5327 /* detailed command descriptions */
5328 NmcEditorSubCmd cmdsub = parse_editor_sub_cmd (command, NULL);
5329
5330 switch (cmdsub) {
5331 case NMC_EDITOR_SUB_CMD_SET:
5332 printf (_("set [<value>] :: set new value\n\n"
5333 "This command sets provided <value> to this property\n"));
5334 break;
5335 case NMC_EDITOR_SUB_CMD_ADD:
5336 printf (_("add [<value>] :: add new option to the property\n\n"
5337 "This command add provided <value> to this property, if "
5338 "the property is of a container type. For single-valued "
5339 "properties it replaces the value (same as 'set').\n"));
5340 break;
5341 case NMC_EDITOR_SUB_CMD_CHANGE:
5342 printf (_("change :: change current value\n\n"
5343 "Displays current value and allows editing it.\n"));
5344 break;
5345 case NMC_EDITOR_SUB_CMD_REMOVE:
5346 printf (_("remove [<index>|<option>] :: delete the value\n\n"
5347 "Removes the property value (sets it to default).\n"));
5348 break;
5349 case NMC_EDITOR_SUB_CMD_DESCRIBE:
5350 printf (_("describe :: describe property\n\n"
5351 "Shows property description. You can consult nm-settings(5) "
5352 "manual page to see all NM settings and properties.\n"));
5353 break;
5354 case NMC_EDITOR_SUB_CMD_PRINT:
5355 printf (_("print [property|setting|connection] :: print property (setting, connection) value(s)\n\n"
5356 "Shows property value. Providing an argument you can also display "
5357 "values for the whole setting or connection.\n"));
5358 break;
5359 case NMC_EDITOR_SUB_CMD_BACK:
5360 printf (_("back :: go to upper menu level\n\n"));
5361 break;
5362 case NMC_EDITOR_SUB_CMD_HELP:
5363 printf (_("help/? [<command>] :: help for nmcli commands\n\n"));
5364 break;
5365 case NMC_EDITOR_SUB_CMD_QUIT:
5366 printf (_("quit :: exit nmcli\n\n"
5367 "This command exits nmcli. When the connection being edited "
5368 "is not saved, the user is asked to confirm the action.\n"));
5369 break;
5370 default:
5371 printf (_("Unknown command: '%s'\n"), command);
5372 break;
5373 }
5374 }
5375 }
5376
5377 /*----------------------------------------------------------------------------*/
5378
5379 static gboolean nmc_editor_cb_called;
5380 static GError *nmc_editor_error;
5381 static GMutex nmc_editor_mutex;
5382 static GCond nmc_editor_cond;
5383
5384 /*
5385 * Store 'error' to shared 'nmc_editor_error' and signal the condition
5386 * so that the 'editor-thread' thread could process that.
5387 */
5388 static void
5389 set_and_signal_error (GError *error)
5390 {
5391 g_mutex_lock (&nmc_editor_mutex);
5392 nmc_editor_cb_called = TRUE;
5393 nmc_editor_error = error ? g_error_copy (error) : NULL;
5394 g_cond_signal (&nmc_editor_cond);
5395 g_mutex_unlock (&nmc_editor_mutex);
5396 }
5397
5398 static void
5399 add_connection_editor_cb (NMRemoteSettings *settings,
5400 NMRemoteConnection *connection,
5401 GError *error,
5402 gpointer user_data)
5403 {
5404 set_and_signal_error (error);
5405 }
5406
5407 static void
5408 update_connection_editor_cb (NMRemoteConnection *connection,
5409 GError *error,
5410 gpointer user_data)
5411 {
5412 set_and_signal_error (error);
5413 }
5414
5415 /*----------------------------------------------------------------------------*/
5416
5417 static void
5418 print_property_description (NMSetting *setting, const char *prop_name)
5419 {
5420 char *desc;
5421
5422 desc = nmc_setting_get_property_desc (setting, prop_name);
5423 printf ("\n=== [%s] ===\n%s\n", prop_name, desc);
5424 g_free (desc);
5425 }
5426
5427 static void
5428 print_setting_description (NMSetting *setting)
5429 {
5430 /* Show description of all properties */
5431 char **all_props;
5432 int i;
5433
5434 all_props = nmc_setting_get_valid_properties (setting);
5435 printf (("<<< %s >>>\n"), nm_setting_get_name (setting));
5436 for (i = 0; all_props && all_props[i]; i++)
5437 print_property_description (setting, all_props[i]);
5438 g_strfreev (all_props);
5439 }
5440
5441 static gboolean
5442 connection_remove_setting (NMConnection *connection, NMSetting *setting)
5443 {
5444 gboolean mandatory;
5445
5446 mandatory = is_setting_mandatory (connection, setting);
5447 if (!mandatory) {
5448 nm_connection_remove_setting (connection, G_OBJECT_TYPE (setting));
5449 return TRUE;
5450 }
5451 printf (_("Error: setting '%s' is mandatory and cannot be removed.\n"),
5452 nm_setting_get_name (setting));
5453 return FALSE;
5454 }
5455
5456 static void
5457 editor_show_status_line (NMConnection *connection, gboolean dirty)
5458 {
5459 NMSettingConnection *s_con;
5460 const char *con_type, *con_id, *con_uuid;
5461
5462 s_con = nm_connection_get_setting_connection (connection);
5463 g_assert (s_con);
5464 con_type = nm_setting_connection_get_connection_type (s_con);
5465 con_id = nm_connection_get_id (connection);
5466 con_uuid = nm_connection_get_uuid (connection);
5467
5468 /* TRANSLATORS: status line in nmcli connection editor */
5469 printf (_("[ Connection type: %s | name: %s | UUID: %s | dirty: %s ]\n"),
5470 con_type, con_id, con_uuid, dirty ? _("yes") : _("no"));
5471 }
5472
5473 /*
5474 * Submenu for detailed property editing
5475 * Return: TRUE - continue; FALSE - should quit
5476 */
5477 static gboolean
5478 property_edit_submenu (NmCli *nmc,
5479 NMConnection *connection,
5480 NMRemoteConnection *rem_con,
5481 NMSetting *curr_setting,
5482 const char *prop_name)
5483 {
5484 NmcEditorSubCmd cmdsub;
5485 gboolean cmd_property_loop = TRUE;
5486 gboolean should_quit = FALSE;
5487 char *prop_val_user, *tmp_prompt;
5488 gboolean set_result;
5489 GError *tmp_err = NULL;
5490 char *prompt;
5491 gboolean dirty;
5492 GValue prop_g_value = G_VALUE_INIT;
5493
5494 prompt = nmc_colorize (nmc->editor_prompt_color, "nmcli %s.%s> ",
5495 nm_setting_get_name (curr_setting), prop_name);
5496
5497 while (cmd_property_loop) {
5498 char *cmd_property_user;
5499 char *cmd_property_arg;
5500
5501 /* Connection is dirty? (not saved or differs from the saved) */
5502 dirty = !nm_connection_compare (connection,
5503 rem_con ? NM_CONNECTION (rem_con) : NULL,
5504 NM_SETTING_COMPARE_FLAG_EXACT);
5505
5506 if (nmc->editor_status_line)
5507 editor_show_status_line (connection, dirty);
5508
5509 cmd_property_user = readline_x (prompt);
5510 if (!cmd_property_user || *cmd_property_user == '\0')
5511 continue;
5512 cmdsub = parse_editor_sub_cmd (g_strstrip (cmd_property_user), &cmd_property_arg);
5513
5514 switch (cmdsub) {
5515 case NMC_EDITOR_SUB_CMD_SET:
5516 case NMC_EDITOR_SUB_CMD_ADD:
5517 /* list, arrays,...: SET replaces the whole property value
5518 * ADD adds the new value(s)
5519 * single values: : both SET and ADD sets the new value
5520 */
5521 if (!cmd_property_arg) {
5522 tmp_prompt = g_strdup_printf (_("Enter '%s' value: "), prop_name);
5523 prop_val_user = readline_x (tmp_prompt);
5524 g_free (tmp_prompt);
5525 } else
5526 prop_val_user = g_strdup (cmd_property_arg);
5527
5528 /* nmc_setting_set_property() only adds new value, thus we have to
5529 * remove the original value and save it for error cases.
5530 */
5531 if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
5532 nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
5533 nmc_property_set_default_value (curr_setting, prop_name);
5534 }
5535
5536 set_result = nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err);
5537 g_free (prop_val_user);
5538 if (!set_result) {
5539 printf (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
5540 g_clear_error (&tmp_err);
5541 if (cmdsub == NMC_EDITOR_SUB_CMD_SET)
5542 nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
5543 }
5544 if (G_IS_VALUE (&prop_g_value))
5545 g_value_unset (&prop_g_value);
5546 break;
5547
5548 case NMC_EDITOR_SUB_CMD_CHANGE:
5549 *edit_lib_symbols.rl_startup_hook_x = set_deftext;
5550 pre_input_deftext = nmc_setting_get_property_out2in (curr_setting, prop_name, NULL);
5551 tmp_prompt = g_strdup_printf (_("Edit '%s' value: "), prop_name);
5552 prop_val_user = readline_x (tmp_prompt);
5553
5554 nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
5555 nmc_property_set_default_value (curr_setting, prop_name);
5556
5557 if (!nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err)) {
5558 printf (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
5559 g_clear_error (&tmp_err);
5560 nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
5561 }
5562 g_free (prop_val_user);
5563 g_free (tmp_prompt);
5564 if (G_IS_VALUE (&prop_g_value))
5565 g_value_unset (&prop_g_value);
5566 break;
5567
5568 case NMC_EDITOR_SUB_CMD_REMOVE:
5569 if (cmd_property_arg) {
5570 unsigned long val_int = G_MAXUINT32;
5571 char *option = NULL;
5572
5573 if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int))
5574 option = g_strdup (cmd_property_arg);
5575
5576 if (!nmc_setting_remove_property_option (curr_setting, prop_name,
5577 option ? g_strstrip (option) : NULL,
5578 (guint32) val_int,
5579 &tmp_err)) {
5580 printf (_("Error: %s\n"), tmp_err->message);
5581 g_clear_error (&tmp_err);
5582 }
5583 g_free (option);
5584 } else {
5585 if (!nmc_setting_reset_property (curr_setting, prop_name, &tmp_err)) {
5586 printf (_("Error: failed to remove value of '%s': %s\n"), prop_name,
5587 tmp_err->message);
5588 g_clear_error (&tmp_err);
5589 }
5590 }
5591 break;
5592
5593 case NMC_EDITOR_SUB_CMD_DESCRIBE:
5594 /* Show property description */
5595 print_property_description (curr_setting, prop_name);
5596 break;
5597
5598 case NMC_EDITOR_SUB_CMD_PRINT:
5599 /* Print current connection settings/properties */
5600 if (cmd_property_arg) {
5601 if (matches (cmd_property_arg, "setting") == 0)
5602 editor_show_setting (curr_setting, nmc);
5603 else if ( matches (cmd_property_arg, "connection") == 0
5604 || matches (cmd_property_arg, "all") == 0)
5605 editor_show_connection (connection, nmc);
5606 else
5607 printf (_("Unknown command argument: '%s'\n"), cmd_property_arg);
5608 } else {
5609 char *prop_val = nmc_setting_get_property (curr_setting, prop_name, NULL);
5610 printf ("%s: %s\n", prop_name, prop_val);
5611 g_free (prop_val);
5612 }
5613 break;
5614
5615 case NMC_EDITOR_SUB_CMD_BACK:
5616 cmd_property_loop = FALSE;
5617 break;
5618
5619 case NMC_EDITOR_SUB_CMD_HELP:
5620 editor_sub_usage (cmd_property_arg);
5621 break;
5622
5623 case NMC_EDITOR_SUB_CMD_QUIT:
5624 if (dirty) {
5625 char *tmp_str;
5626 do {
5627 tmp_str = nmc_get_user_input (_("The connection is not saved. "
5628 "Do you really want to quit? [y/n]\n"));
5629 } while (!tmp_str);
5630 if (matches (tmp_str, "yes") == 0) {
5631 cmd_property_loop = FALSE;
5632 should_quit = TRUE; /* we will quit nmcli */
5633 }
5634 g_free (tmp_str);
5635 } else {
5636 cmd_property_loop = FALSE;
5637 should_quit = TRUE; /* we will quit nmcli */
5638 }
5639 break;
5640
5641 case NMC_EDITOR_SUB_CMD_UNKNOWN:
5642 default:
5643 printf (_("Unknown command: '%s'\n"), cmd_property_user);
5644 break;
5645 }
5646 g_free (cmd_property_user);
5647 g_free (cmd_property_arg);
5648 }
5649 g_free (prompt);
5650
5651 return !should_quit;
5652 }
5653
5654 /*
5655 * Split 'str' in the following format: [[[setting.]property] [value]]
5656 * and return the components in 'setting', 'property' and 'value'
5657 * Use g_free() to deallocate the returned strings.
5658 */
5659 static void
5660 split_editor_main_cmd_args (const char *str, char **setting, char **property, char **value)
5661 {
5662 char **args, **items;
5663
5664 if (!str)
5665 return;
5666
5667 args = nmc_strsplit_set (str, " \t", 2);
5668 if (args[0]) {
5669 items = nmc_strsplit_set (args[0], ".", 2);
5670 if (g_strv_length (items) == 2) {
5671 if (setting)
5672 *setting = g_strdup (items[0]);
5673 if (property)
5674 *property = g_strdup (items[1]);
5675 } else {
5676 if (property)
5677 *property = g_strdup (items[0]);
5678 }
5679 g_strfreev (items);
5680
5681 if (value && args[1])
5682 *value = g_strstrip (g_strdup (args[1]));
5683 }
5684 g_strfreev (args);
5685 }
5686
5687 static NMSetting *
5688 is_setting_valid (NMConnection *connection, const NameItem *valid_settings, char *setting)
5689 {
5690 const char *setting_name;
5691
5692 if (!(setting_name = check_valid_name (setting, valid_settings, NULL)))
5693 return NULL;
5694 return nm_connection_get_setting_by_name (connection, setting_name);
5695 }
5696
5697 static char *
5698 is_property_valid (NMSetting *setting, const char *property, GError **error)
5699 {
5700 char **valid_props = NULL;
5701 const char *prop_name;
5702 char *ret;
5703
5704 valid_props = nmc_setting_get_valid_properties (setting);
5705 prop_name = nmc_string_is_valid (property, (const char **) valid_props, error);
5706 ret = prop_name ? g_strdup (prop_name) : NULL;
5707 g_strfreev (valid_props);
5708 return ret;
5709 }
5710
5711 static NMSetting *
5712 create_setting_by_name (const char *name, const NameItem *valid_settings)
5713 {
5714 const char *setting_name;
5715 NMSetting *setting = NULL;
5716
5717 /* Get a valid setting name */
5718 setting_name = check_valid_name (name, valid_settings, NULL);
5719
5720 if (setting_name) {
5721 setting = nmc_setting_new_for_name (setting_name);
5722 if (!setting)
5723 return NULL; /* This should really not happen */
5724 nmc_setting_custom_init (setting);
5725 }
5726 return setting;
5727 }
5728
5729 static const char *
5730 ask_check_setting (const char *arg,
5731 const NameItem *valid_settings_arr,
5732 const char *valid_settings_str)
5733 {
5734 char *setting_name_user;
5735 const char *setting_name;
5736 GError *err = NULL;
5737
5738 if (!arg) {
5739 printf (_("Available settings: %s\n"), valid_settings_str);
5740 setting_name_user = nmc_get_user_input (EDITOR_PROMPT_SETTING);
5741 } else
5742 setting_name_user = g_strdup (arg);
5743
5744 if (setting_name_user)
5745 g_strstrip (setting_name_user);
5746
5747 if (!(setting_name = check_valid_name (setting_name_user, valid_settings_arr, &err))) {
5748 printf (_("Error: invalid setting name; %s\n"), err->message);
5749 g_clear_error (&err);
5750 }
5751 g_free (setting_name_user);
5752 return setting_name;
5753 }
5754
5755 static const char *
5756 ask_check_property (const char *arg,
5757 const char **valid_props,
5758 const char *valid_props_str)
5759 {
5760 char *prop_name_user;
5761 const char *prop_name;
5762 GError *tmp_err = NULL;
5763
5764 if (!arg) {
5765 printf (_("Available properties: %s\n"), valid_props_str);
5766 prop_name_user = readline_x (EDITOR_PROMPT_PROPERTY);
5767 if (prop_name_user)
5768 g_strstrip (prop_name_user);
5769 } else
5770 prop_name_user = g_strdup (arg);
5771
5772 if (!(prop_name = nmc_string_is_valid (prop_name_user, valid_props, &tmp_err))) {
5773 printf (_("Error: property %s\n"), tmp_err->message);
5774 g_clear_error (&tmp_err);
5775 }
5776 g_free (prop_name_user);
5777 return prop_name;
5778 }
5779
5780 static gboolean
5781 confirm_connection_saving (NMConnection *local, NMConnection *remote)
5782 {
5783 NMSettingConnection *s_con_loc, *s_con_rem;
5784 gboolean ac_local, ac_remote;
5785 gboolean confirmed = TRUE;
5786
5787 s_con_loc = nm_connection_get_setting_connection (local);
5788 g_assert (s_con_loc);
5789 ac_local = nm_setting_connection_get_autoconnect (s_con_loc);
5790
5791 if (remote) {
5792 s_con_rem = nm_connection_get_setting_connection (remote);
5793 g_assert (s_con_rem);
5794 ac_remote = nm_setting_connection_get_autoconnect (s_con_rem);
5795 } else
5796 ac_remote = FALSE;
5797
5798 if (ac_local && !ac_remote) {
5799 char *answer;
5800 answer = nmc_get_user_input (_("Saving the connection with 'autoconnect=yes'. "
5801 "That might result in an immediate activation of the connection.\n"
5802 "Do you still want to save? [yes] "));
5803 if (!answer || matches (answer, "yes") == 0)
5804 confirmed = TRUE;
5805 else
5806 confirmed = FALSE;
5807 g_free (answer);
5808 }
5809 return confirmed;
5810 }
5811
5812 typedef struct {
5813 guint level;
5814 char *main_prompt;
5815 NMSetting *curr_setting;
5816 char **valid_props;
5817 char *valid_props_str;
5818 } NmcEditorMenuContext;
5819
5820 static void
5821 menu_switch_to_level0 (NmcEditorMenuContext *menu_ctx,
5822 const char *prompt,
5823 NmcTermColor prompt_color)
5824 {
5825 menu_ctx->level = 0;
5826 g_free (menu_ctx->main_prompt);
5827 menu_ctx->main_prompt = nmc_colorize (prompt_color, "%s", prompt);
5828 menu_ctx->curr_setting = NULL;
5829 g_strfreev (menu_ctx->valid_props);
5830 menu_ctx->valid_props = NULL;
5831 g_free (menu_ctx->valid_props_str);
5832 menu_ctx->valid_props_str = NULL;
5833 }
5834
5835 static void
5836 menu_switch_to_level1 (NmcEditorMenuContext *menu_ctx,
5837 NMSetting *setting,
5838 const char *setting_name,
5839 NmcTermColor prompt_color)
5840 {
5841 menu_ctx->level = 1;
5842 g_free (menu_ctx->main_prompt);
5843 menu_ctx->main_prompt = nmc_colorize (prompt_color, "nmcli %s> ", setting_name);
5844 menu_ctx->curr_setting = setting;
5845 g_strfreev (menu_ctx->valid_props);
5846 menu_ctx->valid_props = nmc_setting_get_valid_properties (menu_ctx->curr_setting);
5847 g_free (menu_ctx->valid_props_str);
5848 menu_ctx->valid_props_str = g_strjoinv (", ", menu_ctx->valid_props);
5849 }
5850
5851 static gboolean
5852 editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type)
5853 {
5854 NMRemoteConnection *rem_con = NULL;
5855 NmcEditorMainCmd cmd;
5856 char *cmd_user;
5857 gboolean cmd_loop = TRUE;
5858 char *cmd_arg = NULL;
5859 char *cmd_arg_s, *cmd_arg_p, *cmd_arg_v;
5860 const char *BASE_PROMPT = "nmcli> ";
5861 const NameItem *valid_settings_arr = NULL;
5862 char *valid_settings_str = NULL;
5863 AddConnectionInfo *info = NULL;
5864 gboolean dirty;
5865 GError *err1 = NULL;
5866 NmcEditorMenuContext menu_ctx;
5867
5868 valid_settings_arr = get_valid_settings_array (connection_type);
5869 valid_settings_str = get_valid_options_string (valid_settings_arr);
5870 printf (_("You may edit the following settings: %s\n"), valid_settings_str);
5871
5872 menu_ctx.level = 0;
5873 menu_ctx.main_prompt = nmc_colorize (nmc->editor_prompt_color, BASE_PROMPT);
5874 menu_ctx.curr_setting = NULL;
5875 menu_ctx.valid_props = NULL;
5876 menu_ctx.valid_props_str = NULL;
5877
5878 while (cmd_loop) {
5879 if (!rem_con)
5880 rem_con = nm_remote_settings_get_connection_by_uuid (nmc->system_settings,
5881 nm_connection_get_uuid (connection));
5882
5883 /* Connection is dirty? (not saved or differs from the saved) */
5884 dirty = !nm_connection_compare (connection,
5885 rem_con ? NM_CONNECTION (rem_con) : NULL,
5886 NM_SETTING_COMPARE_FLAG_EXACT);
5887 if (nmc->editor_status_line)
5888 editor_show_status_line (connection, dirty);
5889
5890 cmd_user = readline_x (menu_ctx.main_prompt);
5891 if (!cmd_user || *cmd_user == '\0')
5892 continue;
5893 cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg);
5894
5895 cmd_arg_s = NULL;
5896 cmd_arg_p = NULL;
5897 cmd_arg_v = NULL;
5898 split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v);
5899 switch (cmd) {
5900 case NMC_EDITOR_MAIN_CMD_SET:
5901 /* Set property value */
5902 if (!cmd_arg) {
5903 if (menu_ctx.level == 1) {
5904 const char *prop_name;
5905 char *prop_val_user = NULL;
5906 char *tmp_prompt;
5907 const char *avals;
5908 GError *tmp_err = NULL;
5909
5910 prop_name = ask_check_property (cmd_arg,
5911 (const char **) menu_ctx.valid_props,
5912 menu_ctx.valid_props_str);
5913 if (!prop_name)
5914 break;
5915
5916 avals = nmc_setting_get_property_allowed_values (menu_ctx.curr_setting, prop_name);
5917 if (avals)
5918 printf (_("Allowed values for '%s' property: %s\n"), prop_name, avals);
5919
5920 tmp_prompt = g_strdup_printf (_("Enter '%s' value: "), prop_name);
5921 prop_val_user = readline_x (tmp_prompt);
5922 g_free (tmp_prompt);
5923
5924 /* Set property value */
5925 if (!nmc_setting_set_property (menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) {
5926 printf (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
5927 g_clear_error (&tmp_err);
5928 }
5929 } else {
5930 printf (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
5931 printf (_("use 'goto <setting>' first, or 'set <setting>.<property>'\n"));
5932 }
5933 } else {
5934 NMSetting *ss = NULL;
5935 gboolean created_ss = FALSE;
5936 char *prop_name;
5937 char *tmp_prompt;
5938 GError *tmp_err = NULL;
5939
5940 if (cmd_arg_s) {
5941 /* setting provided as "setting.property" */
5942 ss = is_setting_valid (connection, valid_settings_arr, cmd_arg_s);
5943 if (!ss) {
5944 ss = create_setting_by_name (cmd_arg_s, valid_settings_arr);
5945 if (!ss) {
5946 printf (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
5947 cmd_arg_s, valid_settings_str);
5948 break;
5949 }
5950 created_ss = TRUE;
5951 }
5952 } else {
5953 if (menu_ctx.curr_setting)
5954 ss = menu_ctx.curr_setting;
5955 else {
5956 printf (_("Error: missing setting for '%s' property\n"), cmd_arg_p);
5957 break;
5958 }
5959 }
5960
5961 prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
5962 if (!prop_name) {
5963 printf (_("Error: invalid property: %s\n"), tmp_err->message);
5964 g_clear_error (&tmp_err);
5965 if (created_ss)
5966 g_object_unref (ss);
5967 break;
5968 }
5969
5970
5971
5972 /* Ask for value */
5973 if (!cmd_arg_v) {
5974 const char *avals = nmc_setting_get_property_allowed_values (ss, prop_name);
5975 if (avals)
5976 printf (_("Allowed values for '%s' property: %s\n"), prop_name, avals);
5977
5978 tmp_prompt = g_strdup_printf (_("Enter '%s' value: "), prop_name);
5979 cmd_arg_v = readline_x (tmp_prompt);
5980 g_free (tmp_prompt);
5981 }
5982
5983 /* Set property value */
5984 if (!nmc_setting_set_property (ss, prop_name, cmd_arg_v, &tmp_err)) {
5985 printf (_("Error: failed to set '%s' property: %s\n"),
5986 prop_name, tmp_err->message);
5987 g_clear_error (&tmp_err);
5988 }
5989
5990 if (created_ss)
5991 nm_connection_add_setting (connection, ss);
5992 g_free (prop_name);
5993 }
5994 break;
5995
5996 case NMC_EDITOR_MAIN_CMD_GOTO:
5997 /* cmd_arg_s != NULL means 'setting.property' argument */
5998 if (menu_ctx.level == 0 || cmd_arg_s) {
5999 /* in top level - no setting selected yet */
6000 const char *setting_name;
6001 NMSetting *setting;
6002 const char *user_arg = cmd_arg_s ? cmd_arg_s : cmd_arg_p;
6003
6004 setting_name = ask_check_setting (user_arg, valid_settings_arr, valid_settings_str);
6005 if (!setting_name)
6006 break;
6007
6008 setting = nm_connection_get_setting_by_name (connection, setting_name);
6009 if (!setting) {
6010 setting = nmc_setting_new_for_name (setting_name);
6011 if (!setting) {
6012 printf (_("Error: unknown setting '%s'\n"), setting_name);
6013 break;
6014 }
6015 nmc_setting_custom_init (setting);
6016 nm_connection_add_setting (connection, setting);
6017 }
6018 /* Set global variable for use in TAB completion */
6019 nmc_completion_setting = setting;
6020
6021 /* Switch to level 1 */
6022 menu_switch_to_level1 (&menu_ctx, setting, setting_name, nmc->editor_prompt_color);
6023
6024 if (!cmd_arg_s) {
6025 printf (_("You may edit the following properties: %s\n"), menu_ctx.valid_props_str);
6026 break;
6027 }
6028 }
6029 if (menu_ctx.level == 1 || cmd_arg_s) {
6030 /* level 1 - setting selected */
6031 const char *prop_name;
6032
6033 prop_name = ask_check_property (cmd_arg_p,
6034 (const char **) menu_ctx.valid_props,
6035 menu_ctx.valid_props_str);
6036 if (!prop_name)
6037 break;
6038
6039 /* submenu - level 2 - editing properties */
6040 cmd_loop = property_edit_submenu (nmc, connection, rem_con, menu_ctx.curr_setting, prop_name);
6041 }
6042 break;
6043
6044 case NMC_EDITOR_MAIN_CMD_REMOVE:
6045 /* Remove setting from connection, or delete value of a property */
6046 if (!cmd_arg) {
6047 if (menu_ctx.level == 1) {
6048 GError *tmp_err = NULL;
6049 const char *prop_name;
6050
6051 prop_name = ask_check_property (cmd_arg,
6052 (const char **) menu_ctx.valid_props,
6053 menu_ctx.valid_props_str);
6054 if (!prop_name)
6055 break;
6056
6057 /* Delete property value */
6058 if (!nmc_setting_reset_property (menu_ctx.curr_setting, prop_name, &tmp_err)) {
6059 printf (_("Error: failed to remove value of '%s': %s\n"), prop_name,
6060 tmp_err->message);
6061 g_clear_error (&tmp_err);
6062 }
6063 } else
6064 printf (_("Error: no argument given; valid are [%s]\n"), valid_settings_str);
6065 } else {
6066 NMSetting *ss = NULL;
6067 gboolean descr_all;
6068 char *user_s;
6069
6070 /* cmd_arg_s != NULL means argument is "setting.property" */
6071 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
6072 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
6073 if (user_s) {
6074 ss = is_setting_valid (connection, valid_settings_arr, user_s);
6075 if (!ss) {
6076 if (check_valid_name (user_s, valid_settings_arr, NULL))
6077 printf (_("Setting '%s' is not present in the connection.\n"), user_s);
6078 else
6079 printf (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
6080 user_s, valid_settings_str);
6081 break;
6082 }
6083 } else
6084 ss = menu_ctx.curr_setting;
6085
6086 if (descr_all) {
6087 /* Remove setting from the connection */
6088 connection_remove_setting (connection, ss);
6089 if (ss == menu_ctx.curr_setting) {
6090 /* If we removed the setting we are in, go up */
6091 menu_switch_to_level0 (&menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
6092 nmc_completion_setting = NULL; /* for TAB completion */
6093 }
6094 } else {
6095 GError *tmp_err = NULL;
6096 char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
6097 if (prop_name) {
6098 /* Delete property value */
6099 if (!nmc_setting_reset_property (ss, prop_name, &tmp_err)) {
6100 printf (_("Error: failed to remove value of '%s': %s\n"), prop_name,
6101 tmp_err->message);
6102 g_clear_error (&tmp_err);
6103 }
6104 } else {
6105 /* If the string is not a property, try it as a setting */
6106 NMSetting *s_tmp;
6107 s_tmp = is_setting_valid (connection, valid_settings_arr, cmd_arg_p);
6108 if (s_tmp) {
6109 /* Remove setting from the connection */
6110 connection_remove_setting (connection, s_tmp);
6111 if (ss == menu_ctx.curr_setting) {
6112 /* If we removed the setting we are in, go up */
6113 menu_switch_to_level0 (&menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
6114 nmc_completion_setting = NULL; /* for TAB completion */
6115 }
6116 } else
6117 printf (_("Error: %s properties, nor it is a setting name.\n"),
6118 tmp_err->message);
6119 g_clear_error (&tmp_err);
6120 }
6121 g_free (prop_name);
6122 }
6123 }
6124 break;
6125
6126 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
6127 /* Print property description */
6128 if (!cmd_arg) {
6129 if (menu_ctx.level == 1) {
6130 const char *prop_name;
6131
6132 prop_name = ask_check_property (cmd_arg,
6133 (const char **) menu_ctx.valid_props,
6134 menu_ctx.valid_props_str);
6135 if (!prop_name)
6136 break;
6137
6138 /* Show property description */
6139 print_property_description (menu_ctx.curr_setting, prop_name);
6140 } else {
6141 printf (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
6142 printf (_("use 'goto <setting>' first, or 'describe <setting>.<property>'\n"));
6143 }
6144 } else {
6145 NMSetting *ss = NULL;
6146 gboolean unref_ss = FALSE;
6147 gboolean descr_all;
6148 char *user_s;
6149
6150 /* cmd_arg_s != NULL means argument is "setting.property" */
6151 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
6152 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
6153 if (user_s) {
6154 ss = is_setting_valid (connection, valid_settings_arr, user_s);
6155 if (!ss) {
6156 ss = create_setting_by_name (user_s, valid_settings_arr);
6157 if (!ss) {
6158 printf (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
6159 user_s, valid_settings_str);
6160 break;
6161 }
6162 unref_ss = TRUE;
6163 }
6164 } else
6165 ss = menu_ctx.curr_setting;
6166
6167 if (descr_all) {
6168 /* Show description for all properties */
6169 print_setting_description (ss);
6170 } else {
6171 GError *tmp_err = NULL;
6172 char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
6173 if (prop_name) {
6174 /* Show property description */
6175 print_property_description (ss, prop_name);
6176 } else {
6177 /* If the string is not a property, try it as a setting */
6178 NMSetting *s_tmp;
6179 s_tmp = is_setting_valid (connection, valid_settings_arr, cmd_arg_p);
6180 if (s_tmp)
6181 print_setting_description (s_tmp);
6182 else
6183 printf (_("Error: invalid property: %s, "
6184 "neither a valid setting name.\n"),
6185 tmp_err->message);
6186 g_clear_error (&tmp_err);
6187 }
6188 g_free (prop_name);
6189 }
6190 if (unref_ss)
6191 g_object_unref (ss);
6192 }
6193 break;
6194
6195 case NMC_EDITOR_MAIN_CMD_PRINT:
6196 /* Print current connection settings/properties */
6197 if (cmd_arg) {
6198 if (strcmp (cmd_arg, "all") == 0)
6199 editor_show_connection (connection, nmc);
6200 else {
6201 const char *s = check_valid_name (cmd_arg, valid_settings_arr, NULL);
6202 if (s) {
6203 NMSetting *ss = nm_connection_get_setting_by_name (connection, s);
6204 if (ss)
6205 editor_show_setting (ss, nmc);
6206 else
6207 printf (_("Error: '%s' setting not present\n"), s);
6208 }
6209 else
6210 printf (_("Error: unknown setting: '%s'\n"), cmd_arg);
6211 }
6212 } else {
6213 if (menu_ctx.curr_setting)
6214 editor_show_setting (menu_ctx.curr_setting, nmc);
6215 else
6216 editor_show_connection (connection, nmc);
6217 }
6218 break;
6219
6220 case NMC_EDITOR_MAIN_CMD_VERIFY:
6221 /* Verify current setting or the whole connection */
6222 if ( menu_ctx.curr_setting
6223 && (!cmd_arg || strcmp (cmd_arg, "all") != 0)) {
6224 GError *tmp_err = NULL;
6225 nm_setting_verify (menu_ctx.curr_setting, NULL, &tmp_err);
6226 printf (_("Verify setting '%s': %s\n"),
6227 nm_setting_get_name (menu_ctx.curr_setting),
6228 tmp_err ? tmp_err->message : "OK");
6229 g_clear_error (&tmp_err);
6230 } else {
6231 GError *tmp_err = NULL;
6232 nm_connection_verify (connection, &tmp_err);
6233 printf (_("Verify connection: %s\n"),
6234 tmp_err ? tmp_err->message : "OK");
6235 g_clear_error (&tmp_err);
6236 }
6237 break;
6238
6239 case NMC_EDITOR_MAIN_CMD_SAVE:
6240 /* Save the connection */
6241 if (nm_connection_verify (connection, &err1)) {
6242 /* Ask for save confirmation if the connection changes to autoconnect=yes */
6243 if (nmc->editor_save_confirmation)
6244 if (!confirm_connection_saving (connection, NM_CONNECTION (rem_con)))
6245 break;
6246
6247 if (!rem_con) {
6248 /* Tell the settings service to add the new connection */
6249 info = g_malloc0 (sizeof (AddConnectionInfo));
6250 info->nmc = nmc;
6251 info->con_name = g_strdup (nm_connection_get_id (connection));
6252 nm_remote_settings_add_connection (nmc->system_settings,
6253 connection,
6254 add_connection_editor_cb,
6255 info);
6256 } else {
6257 /* Save/update already saved (existing) connection */
6258 nm_connection_replace_settings_from_connection (NM_CONNECTION (rem_con),
6259 connection,
6260 NULL);
6261 nm_remote_connection_commit_changes (rem_con,
6262 update_connection_editor_cb,
6263 NULL);
6264 }
6265
6266 g_mutex_lock (&nmc_editor_mutex);
6267 //FIXME: add also a timeout for cases the callback is not called
6268 while (!nmc_editor_cb_called)
6269 g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
6270
6271 if (nmc_editor_error) {
6272 printf (_("Error: Failed to save '%s' (%s) connection: (%d) %s\n"),
6273 nm_connection_get_id (connection),
6274 nm_connection_get_uuid (connection),
6275 nmc_editor_error->code, nmc_editor_error->message);
6276
6277 g_error_free (nmc_editor_error);
6278 } else {
6279 NMRemoteConnection *con_tmp;
6280
6281 printf (_("Connection '%s' (%s) successfully saved.\n"),
6282 nm_connection_get_id (connection),
6283 nm_connection_get_uuid (connection));
6284
6285 /* Replace local connection with the remote one to be sure they are equal.
6286 * This mitigates problems with plugins not preserving some properties or
6287 * adding ipv{4,6} settings when not present.
6288 */
6289 con_tmp = nm_remote_settings_get_connection_by_uuid (nmc->system_settings,
6290 nm_connection_get_uuid (connection));
6291 if (con_tmp)
6292 nm_connection_replace_settings_from_connection (connection,
6293 NM_CONNECTION (con_tmp),
6294 NULL);
6295 }
6296
6297 nmc_editor_cb_called = FALSE;
6298 nmc_editor_error = NULL;
6299 g_mutex_unlock (&nmc_editor_mutex);
6300 } else
6301 printf (_("Error: connection verification failed: %s\n"),
6302 err1 ? err1->message : _("(unknown error)"));
6303
6304 g_clear_error (&err1);
6305 break;
6306
6307 case NMC_EDITOR_MAIN_CMD_BACK:
6308 /* Go back (up) an the menu */
6309 if (menu_ctx.level == 1) {
6310 menu_switch_to_level0 (&menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
6311 nmc_completion_setting = NULL; /* for TAB completion */
6312 }
6313 break;
6314
6315 case NMC_EDITOR_MAIN_CMD_HELP:
6316 /* Print command help */
6317 editor_main_help (cmd_arg);
6318 break;
6319
6320 case NMC_EDITOR_MAIN_CMD_NMCLI:
6321 if (cmd_arg_p && matches (cmd_arg_p, "status-line") == 0) {
6322 GError *tmp_err = NULL;
6323 gboolean bb;
6324 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
6325 printf (_("Error: status-line: %s\n"), tmp_err->message);
6326 g_clear_error (&tmp_err);
6327 } else
6328 nmc->editor_status_line = bb;
6329 } else if (cmd_arg_p && matches (cmd_arg_p, "save-confirmation") == 0) {
6330 GError *tmp_err = NULL;
6331 gboolean bb;
6332 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
6333 printf (_("Error: save-confirmation: %s\n"), tmp_err->message);
6334 g_clear_error (&tmp_err);
6335 } else
6336 nmc->editor_save_confirmation = bb;
6337 } else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color") == 0) {
6338 unsigned long color;
6339 if (!nmc_string_to_uint (cmd_arg_v ? g_strstrip (cmd_arg_v) : "X",
6340 TRUE, 0, 8, &color))
6341 printf (_("Error: bad color number: '%s'; use <0-8>\n"),
6342 cmd_arg_v ? cmd_arg_v : "");
6343 else {
6344 nmc->editor_prompt_color = color;
6345 g_free (menu_ctx.main_prompt);
6346 if (menu_ctx.level == 0)
6347 menu_ctx.main_prompt = nmc_colorize (nmc->editor_prompt_color, BASE_PROMPT);
6348 else
6349 menu_ctx.main_prompt = nmc_colorize (nmc->editor_prompt_color, "nmcli %s> ",
6350 nm_setting_get_name (menu_ctx.curr_setting));
6351 }
6352 } else if (!cmd_arg_p) {
6353 printf (_("Current nmcli configuration:\n"));
6354 printf ("status-line: %s\n"
6355 "save-confirmation: %s\n"
6356 "prompt-color: %d\n",
6357 nmc->editor_status_line ? "yes" : "no",
6358 nmc->editor_save_confirmation ? "yes" : "no",
6359 nmc->editor_prompt_color);
6360 } else
6361 printf (_("Invalid configuration option '%s'; allowed [%s]\n"),
6362 cmd_arg_v ? cmd_arg_v : "", "status-line, save-confirmation, prompt-color");
6363
6364 break;
6365
6366 case NMC_EDITOR_MAIN_CMD_QUIT:
6367 if (dirty) {
6368 char *tmp_str;
6369 do {
6370 tmp_str = nmc_get_user_input (_("The connection is not saved. "
6371 "Do you really want to quit? [y/n]\n"));
6372 } while (!tmp_str);
6373 if (matches (tmp_str, "yes") == 0)
6374 cmd_loop = FALSE; /* quit command loop */
6375 g_free (tmp_str);
6376 } else
6377 cmd_loop = FALSE; /* quit command loop */
6378 break;
6379
6380 case NMC_EDITOR_MAIN_CMD_UNKNOWN:
6381 default:
6382 printf (_("Unknown command: '%s'\n"), cmd_user);
6383 break;
6384 }
6385
6386 g_free (cmd_user);
6387 g_free (cmd_arg);
6388 g_free (cmd_arg_s);
6389 g_free (cmd_arg_p);
6390 g_free (cmd_arg_v);
6391 }
6392 g_free (valid_settings_str);
6393 g_free (menu_ctx.main_prompt);
6394 g_strfreev (menu_ctx.valid_props);
6395 g_free (menu_ctx.valid_props_str);
6396
6397 /* Save history file */
6398 save_history_cmds (nm_connection_get_uuid (connection));
6399
6400 return TRUE;
6401 }
6402
6403 static const char *
6404 get_ethernet_device_name (NmCli *nmc)
6405 {
6406 const GPtrArray *devices;
6407 int i;
6408
6409 nmc->get_client (nmc);
6410 devices = nm_client_get_devices (nmc->client);
6411 for (i = 0; devices && (i < devices->len); i++) {
6412 NMDevice *dev = g_ptr_array_index (devices, i);
6413 if (NM_IS_DEVICE_ETHERNET (dev))
6414 return nm_device_get_iface (dev);
6415 }
6416 return NULL;
6417 }
6418
6419 static void
6420 editor_init_new_connection (NmCli *nmc, NMConnection *connection)
6421 {
6422 NMSetting *setting, *base_setting;
6423 NMSettingConnection *s_con;
6424 const char *con_type;
6425 const char *slave_type = NULL;
6426
6427 s_con = nm_connection_get_setting_connection (connection);
6428 g_assert (s_con);
6429 con_type = nm_setting_connection_get_connection_type (s_con);
6430
6431 /* Initialize new connection according to its type using sensible defaults. */
6432
6433 if (g_strcmp0 (con_type, "bond-slave") == 0)
6434 slave_type = NM_SETTING_BOND_SETTING_NAME;
6435 if (g_strcmp0 (con_type, "team-slave") == 0)
6436 slave_type = NM_SETTING_TEAM_SETTING_NAME;
6437 if (g_strcmp0 (con_type, "bridge-slave") == 0)
6438 slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
6439
6440 if (slave_type) {
6441 const char *dev_ifname = get_ethernet_device_name (nmc);
6442
6443 /* For bond/team/bridge slaves add 'wired' setting */
6444 setting = nm_setting_wired_new ();
6445 nm_connection_add_setting (connection, setting);
6446
6447 g_object_set (s_con,
6448 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
6449 NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
6450 NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
6451 NULL);
6452 } else {
6453 /* Add a "base" setting to the connection by default */
6454 base_setting = nmc_setting_new_for_name (con_type);
6455 if (!base_setting)
6456 return;
6457 nm_connection_add_setting (connection, base_setting);
6458
6459 /* Set a sensible bond/bridge interface name by default */
6460 if (g_strcmp0 (con_type, NM_SETTING_BOND_SETTING_NAME) == 0)
6461 g_object_set (NM_SETTING_BOND (base_setting),
6462 NM_SETTING_BOND_INTERFACE_NAME, "nm-bond",
6463 NULL);
6464 if (g_strcmp0 (con_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0)
6465 g_object_set (NM_SETTING_BRIDGE (base_setting),
6466 NM_SETTING_BRIDGE_INTERFACE_NAME, "nm-bridge",
6467 NULL);
6468
6469 /* Set sensible initial VLAN values */
6470 if (g_strcmp0 (con_type, NM_SETTING_VLAN_SETTING_NAME) == 0) {
6471 const char *dev_ifname = get_ethernet_device_name (nmc);
6472
6473 g_object_set (NM_SETTING_VLAN (base_setting),
6474 NM_SETTING_VLAN_PARENT, dev_ifname ? dev_ifname : "eth0",
6475 NM_SETTING_VLAN_ID, 1,
6476 NULL);
6477 g_object_set (s_con,
6478 NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
6479 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_VLAN_SETTING_NAME,
6480 NULL);
6481 }
6482
6483 /* Initialize 'transport-mode' so that 'infiniband' is valid */
6484 if (g_strcmp0 (con_type, NM_SETTING_INFINIBAND_SETTING_NAME) == 0)
6485 g_object_set (NM_SETTING_INFINIBAND (base_setting),
6486 NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram",
6487 NULL);
6488
6489 /* Initialize 'number' so that 'cdma' is valid */
6490 if (g_strcmp0 (con_type, NM_SETTING_CDMA_SETTING_NAME) == 0)
6491 g_object_set (NM_SETTING_CDMA (base_setting),
6492 NM_SETTING_CDMA_NUMBER, "#777",
6493 NULL);
6494
6495 /* Initialize 'number' so that 'gsm' is valid */
6496 if (g_strcmp0 (con_type, NM_SETTING_GSM_SETTING_NAME) == 0)
6497 g_object_set (NM_SETTING_GSM (base_setting),
6498 NM_SETTING_GSM_NUMBER, "*99#",
6499 NULL);
6500
6501 /* For Wi-Fi set mode to "infrastructure". Even though mode == NULL
6502 * is regarded as "infrastructure", explicit value makes no doubts.
6503 */
6504 if (g_strcmp0 (con_type, NM_SETTING_WIRELESS_SETTING_NAME) == 0)
6505 g_object_set (NM_SETTING_WIRELESS (base_setting),
6506 NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
6507 NULL);
6508
6509 /* Always add IPv4 and IPv6 settings for non-slave connections */
6510 setting = nm_setting_ip4_config_new ();
6511 nmc_setting_custom_init (setting);
6512 nm_connection_add_setting (connection, setting);
6513
6514 setting = nm_setting_ip6_config_new ();
6515 nmc_setting_custom_init (setting);
6516 nm_connection_add_setting (connection, setting);
6517 }
6518 }
6519
6520 static void
6521 editor_init_existing_connection (NMConnection *connection)
6522 {
6523 NMSettingIP4Config *s_ip4;
6524 NMSettingIP6Config *s_ip6;
6525
6526 s_ip4 = nm_connection_get_setting_ip4_config (connection);
6527 s_ip6 = nm_connection_get_setting_ip6_config (connection);
6528
6529 if (s_ip4)
6530 nmc_setting_ip4_connect_handlers (s_ip4);
6531 if (s_ip6)
6532 nmc_setting_ip6_connect_handlers (s_ip6);
6533 }
6534
6535 static NMCResultCode
6536 do_connection_edit (NmCli *nmc, int argc, char **argv)
6537 {
6538 NMConnection *connection = NULL;
6539 NMSettingConnection *s_con;
6540 const char *connection_type;
6541 char *uuid;
6542 char *default_name = NULL;
6543 const char *type = NULL;
6544 char *type_ask = NULL;
6545 const char *con_name = NULL;
6546 const char *con = NULL;
6547 const char *con_id = NULL;
6548 const char *con_uuid = NULL;
6549 const char *con_path = NULL;
6550 const char *selector = NULL;
6551 char *tmp_str;
6552 GError *error = NULL;
6553 GError *err1 = NULL;
6554 GModule *edit_lib_module = NULL;
6555 nmc_arg_t exp_args[] = { {"type", TRUE, &type, FALSE},
6556 {"con-name", TRUE, &con_name, FALSE},
6557 {"id", TRUE, &con_id, FALSE},
6558 {"uuid", TRUE, &con_uuid, FALSE},
6559 {"path", TRUE, &con_path, FALSE},
6560 {NULL} };
6561
6562 nmc->return_value = NMC_RESULT_SUCCESS;
6563
6564 if (argc == 1)
6565 con = *argv;
6566 else {
6567 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, &error)) {
6568 g_string_assign (nmc->return_text, error->message);
6569 nmc->return_value = error->code;
6570 g_clear_error (&error);
6571 goto error;
6572 }
6573 }
6574
6575 /* Load line editing library */
6576 if (!(edit_lib_module = load_cmd_line_edit_lib ())) {
6577 printf (_(">>> Command-line editing is not available. "
6578 "Consider installing a line editing library to enable the feature. <<<\n"
6579 "Supported libraries are:\n"
6580 " - GNU Readline (libreadline) http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html\n"
6581 " - NetBSD Editline (libedit) http://www.thrysoee.dk/editline/\n"));
6582 edit_lib_symbols.readline_func = NULL;
6583 edit_lib_symbols.add_history_func = NULL;
6584 edit_lib_symbols.history_list_func = NULL;
6585 edit_lib_symbols.rl_insert_text_func = NULL;
6586 edit_lib_symbols.rl_startup_hook_x = NULL;
6587 }
6588
6589 if (!con) {
6590 if (con_id && !con_uuid && !con_path) {
6591 con = con_id;
6592 selector = "id";
6593 } else if (con_uuid && !con_id && !con_path) {
6594 con = con_uuid;
6595 selector = "uuid";
6596 } else if (con_path && !con_id && !con_uuid) {
6597 con = con_path;
6598 selector = "path";
6599 } else if (!con_path && !con_id && !con_uuid) {
6600 /* no-op */
6601 } else {
6602 g_string_printf (nmc->return_text,
6603 _("Error: only one of 'id', uuid, or 'path' can be provided."));
6604 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6605 goto error;
6606 }
6607 }
6608
6609 if (con) {
6610 /* Existing connection */
6611 NMConnection *found_con;
6612
6613 found_con = find_connection (nmc->system_connections, selector, con);
6614 if (!found_con) {
6615 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con);
6616 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6617 goto error;
6618 }
6619
6620 /* Duplicate the connection and use that so that we need not
6621 * differentiate existing vs. new later
6622 */
6623 connection = nm_connection_duplicate (found_con);
6624
6625 s_con = nm_connection_get_setting_connection (connection);
6626 g_assert (s_con);
6627 connection_type = nm_setting_connection_get_connection_type (s_con);
6628
6629 if (type)
6630 printf (_("Warning: editing existing connection '%s'; 'type' argument is ignored\n"),
6631 nm_connection_get_id (connection));
6632 if (con_name)
6633 printf (_("Warning: editing existing connection '%s'; 'con-name' argument is ignored\n"),
6634 nm_connection_get_id (connection));
6635
6636 /* Load previously saved history commands for the connection */
6637 load_history_cmds (nm_connection_get_uuid (connection));
6638
6639 editor_init_existing_connection (connection);
6640 } else {
6641 /* New connection */
6642 connection_type = check_valid_name (type, nmc_valid_connection_types, &err1);
6643 tmp_str = get_valid_options_string (nmc_valid_connection_types);
6644
6645 while (!connection_type) {
6646 if (!type)
6647 printf (_("Valid connection types: %s\n"), tmp_str);
6648 else
6649 printf (_("Error: invalid connection type; %s\n"), err1->message);
6650 g_clear_error (&err1);
6651
6652 type_ask = readline_x (EDITOR_PROMPT_CON_TYPE);
6653 type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
6654 connection_type = check_valid_name (type_ask, nmc_valid_connection_types, &err1);
6655 g_free (type_ask);
6656 }
6657 g_free (tmp_str);
6658
6659 /* Create a new connection object */
6660 connection = nm_connection_new ();
6661
6662 /* Build up the 'connection' setting */
6663 s_con = (NMSettingConnection *) nm_setting_connection_new ();
6664 uuid = nm_utils_uuid_generate ();
6665 if (con_name)
6666 default_name = g_strdup (con_name);
6667 else
6668 default_name = unique_connection_name (nmc->system_connections,
6669 get_name_alias (connection_type, nmc_valid_connection_types));
6670
6671 g_object_set (s_con,
6672 NM_SETTING_CONNECTION_ID, default_name,
6673 NM_SETTING_CONNECTION_UUID, uuid,
6674 NM_SETTING_CONNECTION_TYPE, connection_type,
6675 NULL);
6676 g_free (uuid);
6677 g_free (default_name);
6678 nm_connection_add_setting (connection, NM_SETTING (s_con));
6679
6680 /* Initialize the new connection so that it is valid from the start */
6681 editor_init_new_connection (nmc, connection);
6682 }
6683
6684 printf ("\n");
6685 printf (_("===| nmcli interactive connection editor |==="));
6686 printf ("\n\n");
6687 if (con)
6688 printf (_("Editing existing '%s' connection: '%s'"), connection_type, con);
6689 else
6690 printf (_("Adding a new '%s' connection"), connection_type);
6691 printf ("\n\n");
6692 printf (_("Type 'help' or '?' for available commands."));
6693 printf ("\n");
6694 printf (_("Type 'describe [<setting>.<prop>]' for detailed property description."));
6695 printf ("\n\n");
6696
6697 /* Set global variable for use in TAB completion */
6698 nmc_completion_con_type = connection_type;
6699
6700 /* Run menu loop */
6701 editor_menu_main (nmc, connection, connection_type);
6702
6703 if (edit_lib_module)
6704 g_module_close (edit_lib_module);
6705
6706 if (connection)
6707 g_object_unref (connection);
6708
6709 nmc->should_wait = TRUE;
6710 return nmc->return_value;
6711
6712 error:
6713 if (connection)
6714 g_object_unref (connection);
6715 g_free (type_ask);
6716
6717 nmc->should_wait = FALSE;
6718 return nmc->return_value;
6719 }
6720
6721
6722 static void
6723 modify_connection_cb (NMRemoteConnection *connection,
6724 GError *error,
6725 gpointer user_data)
6726 {
6727 NmCli *nmc = (NmCli *) user_data;
6728
6729 if (error) {
6730 g_string_printf (nmc->return_text,
6731 _("Error: Failed to modify connection '%s': (%d) %s"),
6732 nm_connection_get_id (NM_CONNECTION (connection)),
6733 error->code, error->message);
6734 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
6735 } else {
6736 if (nmc->print_output == NMC_PRINT_PRETTY)
6737 printf (_("Connection '%s' (%s) successfully modified.\n"),
6738 nm_connection_get_id (NM_CONNECTION (connection)),
6739 nm_connection_get_uuid (NM_CONNECTION (connection)));
6740 }
6741 quit ();
6742 }
6743
6744 static NMCResultCode
6745 do_connection_modify (NmCli *nmc, int argc, char **argv)
6746 {
6747 NMConnection *connection = NULL;
6748 NMRemoteConnection *rc = NULL;
6749 NMSetting *setting;
6750 NMSettingConnection *s_con;
6751 const char *con_type;
6752 const char *name;
6753 const char *selector = NULL;
6754 const char *set_prop;
6755 char *value = NULL;
6756 char **strv = NULL;
6757 const char *setting_name;
6758 char *property_name = NULL;
6759 GError *error = NULL;
6760
6761 nmc->should_wait = FALSE;
6762
6763 if (argc == 0) {
6764 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
6765 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6766 goto finish;
6767 }
6768 if ( strcmp (*argv, "id") == 0
6769 || strcmp (*argv, "uuid") == 0
6770 || strcmp (*argv, "path") == 0) {
6771
6772 selector = *argv;
6773 if (next_arg (&argc, &argv) != 0) {
6774 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
6775 selector);
6776 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6777 goto finish;
6778 }
6779 name = *argv;
6780 }
6781 name = *argv;
6782 next_arg (&argc, &argv);
6783 set_prop = *argv;
6784 next_arg (&argc, &argv);
6785 value = g_strjoinv (" ", argv);
6786
6787 if (!name) {
6788 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
6789 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6790 goto finish;
6791 }
6792 if (!set_prop) {
6793 g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing."));
6794 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6795 goto finish;
6796 }
6797 /* NULL value means deleting/setting default property value */
6798
6799 /* create NMClient */
6800 nmc->get_client (nmc);
6801
6802 if (!nm_client_get_manager_running (nmc->client)) {
6803 g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
6804 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
6805 goto finish;
6806 }
6807
6808 connection = find_connection (nmc->system_connections, selector, name);
6809 if (!connection) {
6810 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
6811 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6812 goto finish;
6813 }
6814 strv = g_strsplit (set_prop, ".", 2);
6815 if (g_strv_length (strv) != 2) {
6816 g_string_printf (nmc->return_text, _("Error: invalid <setting>.<property> '%s'."),
6817 set_prop);
6818 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6819 goto finish;
6820 }
6821
6822 rc = nm_remote_settings_get_connection_by_uuid (nmc->system_settings,
6823 nm_connection_get_uuid (connection));
6824
6825 s_con = nm_connection_get_setting_connection (NM_CONNECTION (rc));
6826 g_assert (s_con);
6827 con_type = nm_setting_connection_get_connection_type (s_con);
6828
6829 setting_name = check_valid_name (strv[0], get_valid_settings_array (con_type), &error);
6830 if (!setting_name) {
6831 g_string_printf (nmc->return_text, _("Error: invalid or not allowed setting '%s': %s."),
6832 strv[0], error->message);
6833 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6834 goto finish;
6835 }
6836 setting = nm_connection_get_setting_by_name (NM_CONNECTION (rc), setting_name);
6837 if (!setting) {
6838 setting = nmc_setting_new_for_name (setting_name);
6839 if (!setting) {
6840 /* This should really not happen */
6841 g_string_printf (nmc->return_text,
6842 "Error: don't know how to create '%s' setting.",
6843 setting_name);
6844 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
6845 goto finish;
6846 }
6847 nm_connection_add_setting (NM_CONNECTION (rc), setting);
6848 }
6849
6850 property_name = is_property_valid (setting, strv[1], &error);
6851 if (!property_name) {
6852 g_string_printf (nmc->return_text, _("Error: invalid property '%s': %s."),
6853 strv[1], error->message);
6854 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6855 goto finish;
6856 }
6857 if (!nmc_setting_set_property (setting, property_name, value, &error)) {
6858 g_string_printf (nmc->return_text, _("Error: failed to modify %s.%s: %s."),
6859 strv[0], strv[1], error->message);
6860 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6861 goto finish;
6862 }
6863
6864 nm_remote_connection_commit_changes (rc,
6865 modify_connection_cb,
6866 nmc);
6867 finish:
6868 nmc->should_wait = (nmc->return_value == NMC_RESULT_SUCCESS);
6869 g_free (value);
6870 g_free (property_name);
6871 g_strfreev (strv);
6872 g_clear_error (&error);
6873 return nmc->return_value;
6874 }
6875
6876
6877 typedef struct {
6878 NmCli *nmc;
6879 int counter;
6880 } DeleteStateInfo;
6881
6882 static void
6883 delete_cb (NMRemoteConnection *con, GError *err, gpointer user_data)
6884 {
6885 DeleteStateInfo *info = (DeleteStateInfo *) user_data;
6886
6887 if (err) {
6888 g_string_printf (info->nmc->return_text, _("Error: Connection deletion failed: %s"), err->message);
6889 info->nmc->return_value = NMC_RESULT_ERROR_CON_DEL;
6890 }
6891
6892 info->counter--;
6893 if (info->counter == 0) {
6894 g_free (info);
6895 quit ();
6896 }
6897 }
6898
6899 static NMCResultCode
6900 do_connection_delete (NmCli *nmc, int argc, char **argv)
6901 {
6902 NMConnection *connection = NULL;
6903 DeleteStateInfo *del_info = NULL;
6904 char *line = NULL;
6905 char **arg_arr = NULL;
6906 char **arg_ptr = argv;
6907 int arg_num = argc;
6908 GString *invalid_cons = NULL;
6909 gboolean del_info_free = FALSE;
6910
6911 nmc->return_value = NMC_RESULT_SUCCESS;
6912 nmc->should_wait = FALSE;
6913
6914 /* create NMClient */
6915 nmc->get_client (nmc);
6916
6917 if (!nm_client_get_manager_running (nmc->client)) {
6918 g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
6919 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
6920 goto finish;
6921 }
6922
6923 if (argc == 0) {
6924 if (nmc->ask) {
6925 line = nmc_get_user_input (_("Connection (name, UUID, or path): "));
6926 nmc_string_to_arg_array (line, "", &arg_arr, &arg_num);
6927 arg_ptr = arg_arr;
6928 }
6929 if (arg_num == 0) {
6930 g_string_printf (nmc->return_text, _("Error: No connection specified."));
6931 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6932 goto finish;
6933 }
6934 }
6935
6936 del_info = g_malloc0 (sizeof (DeleteStateInfo));
6937 del_info->nmc = nmc;
6938 del_info->counter = 0;
6939 del_info_free = TRUE;
6940
6941 while (arg_num > 0) {
6942 const char *selector = NULL;
6943
6944 if ( strcmp (*arg_ptr, "id") == 0
6945 || strcmp (*arg_ptr, "uuid") == 0
6946 || strcmp (*arg_ptr, "path") == 0) {
6947 selector = *arg_ptr;
6948 if (next_arg (&arg_num, &arg_ptr) != 0) {
6949 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
6950 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6951 goto finish;
6952 }
6953 }
6954
6955 connection = find_connection (nmc->system_connections, selector, *arg_ptr);
6956 if (!connection) {
6957 if (nmc->print_output != NMC_PRINT_TERSE)
6958 printf (_("Error: unknown connection: %s\n"), *arg_ptr);
6959
6960 if (!invalid_cons)
6961 invalid_cons = g_string_new (NULL);
6962 g_string_append_printf (invalid_cons, "'%s', ", *arg_ptr);
6963
6964 /* take the next argument and continue */
6965 next_arg (&arg_num, &arg_ptr);
6966 continue;
6967 }
6968
6969 /* We need to wait a bit so that nmcli's permissions can be checked.
6970 * We will exit when D-Bus return (error) messages are received.
6971 */
6972 nmc->should_wait = TRUE;
6973
6974 /* del_info deallocation is handled in delete_cb() */
6975 del_info_free = FALSE;
6976
6977 del_info->counter++;
6978
6979 /* Delete the connection */
6980 nm_remote_connection_delete (NM_REMOTE_CONNECTION (connection), delete_cb, del_info);
6981
6982 next_arg (&arg_num, &arg_ptr);
6983 }
6984
6985 finish:
6986 if (del_info_free)
6987 g_free (del_info);
6988 g_strfreev (arg_arr);
6989
6990 if (invalid_cons) {
6991 g_string_truncate (invalid_cons, invalid_cons->len-2); /* truncate trailing ", " */
6992 g_string_printf (nmc->return_text, _("Error: cannot delete unknown connection(s): %s."),
6993 invalid_cons->str);
6994 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6995 g_string_free (invalid_cons, TRUE);
6996 }
6997 return nmc->return_value;
6998 }
6999
7000 static NMCResultCode
7001 do_connection_reload (NmCli *nmc, int argc, char **argv)
7002 {
7003 GError *error = NULL;
7004
7005 nmc->return_value = NMC_RESULT_SUCCESS;
7006 nmc->should_wait = FALSE;
7007
7008 if (!nm_client_get_manager_running (nmc->client)) {
7009 g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
7010 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
7011 return nmc->return_value;
7012 }
7013
7014 if (!nm_remote_settings_reload_connections (nmc->system_settings, &error)) {
7015 g_string_printf (nmc->return_text, _("Error: %s."), error->message);
7016 if (error->code == NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE)
7017 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
7018 else
7019 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
7020 g_clear_error (&error);
7021 }
7022
7023 return nmc->return_value;
7024 }
7025
7026
7027 typedef struct {
7028 NmCli *nmc;
7029 int argc;
7030 char **argv;
7031 } NmcEditorThreadData;
7032
7033 static GThread *editor_thread;
7034 static NmcEditorThreadData editor_thread_data;
7035
7036 /*
7037 * We need to run do_connection_edit() in a thread so that
7038 * glib main loop is not blocked and could receive and process D-Bus
7039 * return messages.
7040 */
7041 static gpointer
7042 connection_editor_thread_func (gpointer data)
7043 {
7044 NmcEditorThreadData *td = (NmcEditorThreadData *) data;
7045
7046 /* run editor for editing/adding connections */
7047 td->nmc->return_value = do_connection_edit (td->nmc, td->argc, td->argv);
7048
7049 /* quit glib main loop now that we are with this thread */
7050 quit ();
7051
7052 return NULL;
7053 }
7054
7055 static NMCResultCode
7056 parse_cmd (NmCli *nmc, int argc, char **argv)
7057 {
7058 GError *error = NULL;
7059 int arg_ret;
7060
7061 if (argc == 0) {
7062 if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
7063 goto opt_error;
7064 nmc->return_value = do_connections_show (nmc, argc, argv);
7065 } else {
7066 if (matches (*argv, "show") == 0) {
7067 arg_ret = next_arg (&argc, &argv);
7068 if (arg_ret != 0 || matches (*argv, "configured") == 0) {
7069 next_arg (&argc, &argv);
7070 nmc->return_value = do_connections_show (nmc, argc, argv);
7071 } else if (matches (*argv, "active") == 0) {
7072 if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
7073 goto opt_error;
7074 nmc->return_value = do_connections_show_active (nmc, argc-1, argv+1);
7075 } else {
7076 g_string_printf (nmc->return_text, _("Error: 'configured' or 'active' command is expected for 'connection show'."));
7077 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7078 nmc->should_wait = FALSE;
7079 }
7080 }
7081 else if (matches(*argv, "up") == 0) {
7082 nmc->return_value = do_connection_up (nmc, argc-1, argv+1);
7083 }
7084 else if (matches(*argv, "down") == 0) {
7085 nmc->return_value = do_connection_down (nmc, argc-1, argv+1);
7086 }
7087 else if (matches(*argv, "add") == 0) {
7088 if (nmc_arg_is_help (*(argv+1))) {
7089 usage_connection_add ();
7090 nmc->should_wait = FALSE;
7091 } else
7092 nmc->return_value = do_connection_add (nmc, argc-1, argv+1);
7093 }
7094 else if (matches(*argv, "edit") == 0) {
7095 editor_thread_data.nmc = nmc;
7096 editor_thread_data.argc = argc - 1;
7097 editor_thread_data.argv = argv + 1;
7098 editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data);
7099 g_thread_unref (editor_thread);
7100 }
7101 else if (matches(*argv, "delete") == 0) {
7102 nmc->return_value = do_connection_delete (nmc, argc-1, argv+1);
7103 }
7104 else if (matches(*argv, "reload") == 0) {
7105 nmc->return_value = do_connection_reload (nmc, argc-1, argv+1);
7106 }
7107 else if (matches (*argv, "modify") == 0) {
7108 nmc->return_value = do_connection_modify (nmc, argc-1, argv+1);
7109 }
7110 else if (nmc_arg_is_help (*argv)) {
7111 usage ();
7112 nmc->should_wait = FALSE;
7113 }
7114 else {
7115 usage ();
7116 g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv);
7117 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7118 nmc->should_wait = FALSE;
7119 }
7120 }
7121
7122 return nmc->return_value;
7123
7124 opt_error:
7125 g_string_printf (nmc->return_text, _("Error: %s."), error->message);
7126 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7127 nmc->should_wait = FALSE;
7128 g_error_free (error);
7129 return nmc->return_value;
7130 }
7131
7132 /* callback called when connections are obtained from the settings service */
7133 static void
7134 get_connections_cb (NMRemoteSettings *settings, gpointer user_data)
7135 {
7136 ArgsInfo *args = (ArgsInfo *) user_data;
7137
7138 /* Get the connection list */
7139 args->nmc->system_connections = nm_remote_settings_list_connections (settings);
7140
7141 parse_cmd (args->nmc, args->argc, args->argv);
7142
7143 if (!args->nmc->should_wait)
7144 quit ();
7145 }
7146
7147 /* Entry point function for connections-related commands: 'nmcli connection' */
7148 NMCResultCode
7149 do_connections (NmCli *nmc, int argc, char **argv)
7150 {
7151 int i = 0;
7152 gboolean real_cmd = FALSE;
7153
7154 if (argc == 0)
7155 real_cmd = TRUE;
7156 else {
7157 while (real_con_commands[i] && matches (*argv, real_con_commands[i]) != 0)
7158 i++;
7159 if (real_con_commands[i] != NULL)
7160 real_cmd = TRUE;
7161 }
7162
7163 if (!real_cmd) {
7164 /* no real execution command - no need to get connections */
7165 return parse_cmd (nmc, argc, argv);
7166 } else {
7167 if (!nmc_versions_match (nmc))
7168 return nmc->return_value;
7169
7170 /* Get NMClient object early */
7171 nmc->get_client (nmc);
7172
7173 nmc->should_wait = TRUE;
7174
7175 args_info.nmc = nmc;
7176 args_info.argc = argc;
7177 args_info.argv = argv;
7178
7179 /* get system settings */
7180 if (!(nmc->system_settings = nm_remote_settings_new (NULL))) {
7181 g_string_printf (nmc->return_text, _("Error: Could not get system settings."));
7182 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
7183 nmc->should_wait = FALSE;
7184 return nmc->return_value;
7185 }
7186
7187 /* find out whether settings service is running */
7188 g_object_get (nmc->system_settings, NM_REMOTE_SETTINGS_SERVICE_RUNNING, &nmc->system_settings_running, NULL);
7189
7190 if (!nmc->system_settings_running) {
7191 g_string_printf (nmc->return_text, _("Error: Can't obtain connections: settings service is not running."));
7192 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
7193 nmc->should_wait = FALSE;
7194 return nmc->return_value;
7195 }
7196
7197 /* connect to signal "connections-read" - emitted when connections are fetched and ready */
7198 g_signal_connect (nmc->system_settings, NM_REMOTE_SETTINGS_CONNECTIONS_READ,
7199 G_CALLBACK (get_connections_cb), &args_info);
7200
7201 /* The rest will be done in get_connection_cb() callback.
7202 * We need to wait for signals that connections are read.
7203 */
7204 return NMC_RESULT_SUCCESS;
7205 }
7206 }
7207