connections.c 278 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* nmcli - command-line tool to control NetworkManager
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
17
 * (C) Copyright 2010 - 2014 Red Hat, Inc.
18 19
 */

20 21
#include "config.h"

22 23 24 25 26 27 28
#include <glib.h>
#include <glib/gi18n.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
29
#include <unistd.h>
30 31 32
#include <errno.h>
#include <signal.h>
#include <netinet/ether.h>
33 34
#include <readline/readline.h>
#include <readline/history.h>
35 36 37

#include <nm-client.h>
#include <nm-device-ethernet.h>
38
#include <nm-device-adsl.h>
39
#include <nm-device-wifi.h>
40
#if WITH_WIMAX
41
#include <nm-device-wimax.h>
42
#endif
43
#include <nm-device-modem.h>
44
#include <nm-device-bt.h>
45
#include <nm-device-olpc-mesh.h>
Dan Winship's avatar
Dan Winship committed
46
#include <nm-device-infiniband.h>
Jiří Klimeš's avatar
Jiří Klimeš committed
47
#include <nm-device-bond.h>
Jiri Pirko's avatar
Jiri Pirko committed
48
#include <nm-device-team.h>
Thomas Graf's avatar
Thomas Graf committed
49
#include <nm-device-bridge.h>
Dan Williams's avatar
Dan Williams committed
50
#include <nm-device-vlan.h>
51 52
#include <nm-remote-settings.h>
#include <nm-vpn-connection.h>
Dan Winship's avatar
Dan Winship committed
53
#include <nm-utils.h>
54 55

#include "utils.h"
56
#include "common.h"
57
#include "settings.h"
58 59
#include "connections.h"

Jiri Pirko's avatar
Jiri Pirko committed
60
/* Activation timeout waiting for bond/team/bridge slaves (in seconds) */
61
#define SLAVES_UP_TIMEOUT 10
62

63 64 65 66 67
/* define some prompts for connection editor */
#define EDITOR_PROMPT_SETTING  _("Setting name? ")
#define EDITOR_PROMPT_PROPERTY _("Property name? ")
#define EDITOR_PROMPT_CON_TYPE _("Enter connection type: ")

68 69 70 71 72 73
/* define some other prompts */
#define PROMPT_CON_TYPE    _("Connection type: ")
#define PROMPT_VPN_TYPE    _("VPN type: ")
#define PROMPT_BOND_MASTER _("Bond master: ")
#define PROMPT_TEAM_MASTER _("Team master: ")
#define PROMPT_BRIDGE_MASTER _("Bridge master: ")
74
#define PROMPT_CONNECTION _("Connection (name, UUID, or path): ")
75 76 77 78 79

static const char *nmc_known_vpns[] =
	{ "openvpn", "vpnc", "pptp", "openconnect", "openswan", "libreswan",
	  "ssh", "l2tp", "iodine", NULL };

80
/* Available fields for 'connection show' */
81
static NmcOutputField nmc_fields_con_show[] = {
82 83 84 85 86 87 88 89
	{"NAME",            N_("NAME"),           25},  /* 0 */
	{"UUID",            N_("UUID"),           38},  /* 1 */
	{"TYPE",            N_("TYPE"),           17},  /* 2 */
	{"TIMESTAMP",       N_("TIMESTAMP"),      12},  /* 3 */
	{"TIMESTAMP-REAL",  N_("TIMESTAMP-REAL"), 34},  /* 4 */
	{"AUTOCONNECT",     N_("AUTOCONNECT"),    13},  /* 5 */
	{"READONLY",        N_("READONLY"),       10},  /* 6 */
	{"DBUS-PATH",       N_("DBUS-PATH"),      42},  /* 7 */
90 91 92 93
	{"ACTIVE",          N_("ACTIVE"),         10},  /* 8 */
	{"DEVICE",          N_("DEVICE"),         10},  /* 9 */
	{"STATE",           N_("STATE"),          12},  /* 10 */
	{"ACTIVE-PATH",     N_("ACTIVE-PATH"),    51},  /* 11 */
94
	{NULL,              NULL,                  0}
95
};
96 97 98
#define NMC_FIELDS_CON_SHOW_ALL     "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,READONLY,DBUS-PATH,"\
                                    "ACTIVE,DEVICE,STATE,ACTIVE-PATH"
#define NMC_FIELDS_CON_SHOW_COMMON  "NAME,UUID,TYPE,DEVICE"
99

100
/* Helper macro to define fields */
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
#define SETTING_FIELD(setting, props) { setting, N_(setting), 0, props, NULL, FALSE, FALSE, 0 }

/* defined in settings.c */
extern NmcOutputField nmc_fields_setting_connection[];
extern NmcOutputField nmc_fields_setting_wired[];
extern NmcOutputField nmc_fields_setting_8021X[];
extern NmcOutputField nmc_fields_setting_wireless[];
extern NmcOutputField nmc_fields_setting_wireless_security[];
extern NmcOutputField nmc_fields_setting_ip4_config[];
extern NmcOutputField nmc_fields_setting_ip6_config[];
extern NmcOutputField nmc_fields_setting_serial[];
extern NmcOutputField nmc_fields_setting_ppp[];
extern NmcOutputField nmc_fields_setting_pppoe[];
extern NmcOutputField nmc_fields_setting_adsl[];
extern NmcOutputField nmc_fields_setting_gsm[];
extern NmcOutputField nmc_fields_setting_cdma[];
extern NmcOutputField nmc_fields_setting_bluetooth[];
extern NmcOutputField nmc_fields_setting_olpc_mesh[];
extern NmcOutputField nmc_fields_setting_vpn[];
extern NmcOutputField nmc_fields_setting_wimax[];
extern NmcOutputField nmc_fields_setting_infiniband[];
extern NmcOutputField nmc_fields_setting_bond[];
extern NmcOutputField nmc_fields_setting_vlan[];
extern NmcOutputField nmc_fields_setting_bridge[];
extern NmcOutputField nmc_fields_setting_bridge_port[];
extern NmcOutputField nmc_fields_setting_team[];
extern NmcOutputField nmc_fields_setting_team_port[];
extern NmcOutputField nmc_fields_setting_dcb[];
129

130
/* Available settings for 'connection show <con>' - profile part */
131
static NmcOutputField nmc_fields_settings_names[] = {
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
	SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME,        nmc_fields_setting_connection + 1),        /* 0 */
	SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME,             nmc_fields_setting_wired + 1),             /* 1 */
	SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME,            nmc_fields_setting_8021X + 1),             /* 2 */
	SETTING_FIELD (NM_SETTING_WIRELESS_SETTING_NAME,          nmc_fields_setting_wireless + 1),          /* 3 */
	SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, nmc_fields_setting_wireless_security + 1), /* 4 */
	SETTING_FIELD (NM_SETTING_IP4_CONFIG_SETTING_NAME,        nmc_fields_setting_ip4_config + 1),        /* 5 */
	SETTING_FIELD (NM_SETTING_IP6_CONFIG_SETTING_NAME,        nmc_fields_setting_ip6_config + 1),        /* 6 */
	SETTING_FIELD (NM_SETTING_SERIAL_SETTING_NAME,            nmc_fields_setting_serial + 1),            /* 7 */
	SETTING_FIELD (NM_SETTING_PPP_SETTING_NAME,               nmc_fields_setting_ppp + 1),               /* 8 */
	SETTING_FIELD (NM_SETTING_PPPOE_SETTING_NAME,             nmc_fields_setting_pppoe + 1),             /* 9 */
	SETTING_FIELD (NM_SETTING_GSM_SETTING_NAME,               nmc_fields_setting_gsm + 1),               /* 10 */
	SETTING_FIELD (NM_SETTING_CDMA_SETTING_NAME,              nmc_fields_setting_cdma + 1),              /* 11 */
	SETTING_FIELD (NM_SETTING_BLUETOOTH_SETTING_NAME,         nmc_fields_setting_bluetooth + 1),         /* 12 */
	SETTING_FIELD (NM_SETTING_OLPC_MESH_SETTING_NAME,         nmc_fields_setting_olpc_mesh + 1),         /* 13 */
	SETTING_FIELD (NM_SETTING_VPN_SETTING_NAME,               nmc_fields_setting_vpn + 1),               /* 14 */
	SETTING_FIELD (NM_SETTING_WIMAX_SETTING_NAME,             nmc_fields_setting_wimax + 1),             /* 15 */
	SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME,        nmc_fields_setting_infiniband + 1),        /* 16 */
	SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME,              nmc_fields_setting_bond + 1),              /* 17 */
	SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME,              nmc_fields_setting_vlan + 1),              /* 18 */
	SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME,              nmc_fields_setting_adsl + 1),              /* 19 */
	SETTING_FIELD (NM_SETTING_BRIDGE_SETTING_NAME,            nmc_fields_setting_bridge + 1),            /* 20 */
	SETTING_FIELD (NM_SETTING_BRIDGE_PORT_SETTING_NAME,       nmc_fields_setting_bridge_port + 1),       /* 21 */
	SETTING_FIELD (NM_SETTING_TEAM_SETTING_NAME,              nmc_fields_setting_team + 1),              /* 22 */
	SETTING_FIELD (NM_SETTING_TEAM_PORT_SETTING_NAME,         nmc_fields_setting_team_port + 1),         /* 23 */
	SETTING_FIELD (NM_SETTING_DCB_SETTING_NAME,               nmc_fields_setting_dcb + 1),               /* 24 */
	{NULL, NULL, 0, NULL, NULL, FALSE, FALSE, 0}
158
};
159
#define NMC_FIELDS_SETTINGS_NAMES_ALL_X  NM_SETTING_CONNECTION_SETTING_NAME","\
160 161 162 163 164 165 166 167 168
                                         NM_SETTING_WIRED_SETTING_NAME","\
                                         NM_SETTING_802_1X_SETTING_NAME","\
                                         NM_SETTING_WIRELESS_SETTING_NAME","\
                                         NM_SETTING_WIRELESS_SECURITY_SETTING_NAME","\
                                         NM_SETTING_IP4_CONFIG_SETTING_NAME","\
                                         NM_SETTING_IP6_CONFIG_SETTING_NAME","\
                                         NM_SETTING_SERIAL_SETTING_NAME","\
                                         NM_SETTING_PPP_SETTING_NAME","\
                                         NM_SETTING_PPPOE_SETTING_NAME","\
169
                                         NM_SETTING_ADSL_SETTING_NAME","\
170 171 172 173
                                         NM_SETTING_GSM_SETTING_NAME","\
                                         NM_SETTING_CDMA_SETTING_NAME","\
                                         NM_SETTING_BLUETOOTH_SETTING_NAME","\
                                         NM_SETTING_OLPC_MESH_SETTING_NAME","\
174
                                         NM_SETTING_VPN_SETTING_NAME","\
Jiří Klimeš's avatar
Jiří Klimeš committed
175
                                         NM_SETTING_INFINIBAND_SETTING_NAME","\
Jiří Klimeš's avatar
Jiří Klimeš committed
176
                                         NM_SETTING_BOND_SETTING_NAME","\
Thomas Graf's avatar
Thomas Graf committed
177 178
                                         NM_SETTING_VLAN_SETTING_NAME","\
                                         NM_SETTING_BRIDGE_SETTING_NAME","\
Jiri Pirko's avatar
Jiri Pirko committed
179 180
                                         NM_SETTING_BRIDGE_PORT_SETTING_NAME","\
                                         NM_SETTING_TEAM_SETTING_NAME","\
181 182
                                         NM_SETTING_TEAM_PORT_SETTING_NAME"," \
                                         NM_SETTING_DCB_SETTING_NAME
183 184
#if WITH_WIMAX
#define NMC_FIELDS_SETTINGS_NAMES_ALL    NMC_FIELDS_SETTINGS_NAMES_ALL_X","\
185
                                         NM_SETTING_WIMAX_SETTING_NAME
186 187 188
#else
#define NMC_FIELDS_SETTINGS_NAMES_ALL    NMC_FIELDS_SETTINGS_NAMES_ALL_X
#endif
189

190 191 192 193
/* Active connection data */
/* Available fields for GENERAL group */
static NmcOutputField nmc_fields_con_active_details_general[] = {
	{"GROUP",         N_("GROUP"),         9},  /* 0 */
194 195 196 197 198 199 200 201 202 203 204 205 206
	{"NAME",          N_("NAME"),         25},  /* 1 */
	{"UUID",          N_("UUID"),         38},  /* 2 */
	{"DEVICES",       N_("DEVICES"),      10},  /* 3 */
	{"STATE",         N_("STATE"),        12},  /* 4 */
	{"DEFAULT",       N_("DEFAULT"),       8},  /* 5 */
	{"DEFAULT6",      N_("DEFAULT6"),      9},  /* 6 */
	{"SPEC-OBJECT",   N_("SPEC-OBJECT"),  10},  /* 7 */
	{"VPN",           N_("VPN"),           5},  /* 8 */
	{"DBUS-PATH",     N_("DBUS-PATH"),    51},  /* 9 */
	{"CON-PATH",      N_("CON-PATH"),     44},  /* 10 */
	{"ZONE",          N_("ZONE"),         15},  /* 11 */
	{"MASTER-PATH",   N_("MASTER-PATH"),  44},  /* 12 */
	{NULL,            NULL,                0}
207
};
208 209
#define NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL  "GROUP,NAME,UUID,DEVICES,STATE,DEFAULT,DEFAULT6,"\
                                                   "VPN,ZONE,DBUS-PATH,CON-PATH,SPEC-OBJECT,MASTER-PATH"
210 211 212 213

/* IP group is handled by common.c */

/* Available fields for VPN group */
214
static NmcOutputField nmc_fields_con_active_details_vpn[] = {
215 216 217 218 219 220 221 222
	{"GROUP",     N_("GROUP"),       9},  /* 0 */
	{"TYPE",      N_("TYPE"),       15},  /* 1 */
	{"USERNAME",  N_("USERNAME"),   15},  /* 2 */
	{"GATEWAY",   N_("GATEWAY"),    25},  /* 3 */
	{"BANNER",    N_("BANNER"),    120},  /* 4 */
	{"VPN-STATE", N_("VPN-STATE"),  40},  /* 5 */
	{"CFG",       N_("CFG"),       120},  /* 6 */
	{NULL, NULL, 0}
223
};
224
#define NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL  "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG"
225

226 227 228 229 230 231
/* defined in common.c */
extern NmcOutputField nmc_fields_ip4_config[];
extern NmcOutputField nmc_fields_ip6_config[];
extern NmcOutputField nmc_fields_dhcp4_config[];
extern NmcOutputField nmc_fields_dhcp6_config[];

232
/* Available fields for 'connection show <con>' - active part */
233
static NmcOutputField nmc_fields_con_active_details_groups[] = {
234 235 236 237 238 239
	{"GENERAL",  N_("GENERAL"), 0, nmc_fields_con_active_details_general + 1},  /* 0 */
	{"IP4",      N_("IP4"),     0, nmc_fields_ip4_config + 1                },  /* 1 */
	{"DHCP4",    N_("DHCP4"),   0, nmc_fields_dhcp4_config + 1              },  /* 2 */
	{"IP6",      N_("IP6"),     0, nmc_fields_ip6_config + 1                },  /* 3 */
	{"DHCP6",    N_("DHCP6"),   0, nmc_fields_dhcp6_config + 1              },  /* 4 */
	{"VPN",      N_("VPN"),     0, nmc_fields_con_active_details_vpn + 1    },  /* 5 */
240 241 242 243
	{NULL, NULL, 0, NULL}
};
#define NMC_FIELDS_CON_ACTIVE_DETAILS_ALL  "GENERAL,IP4,DHCP4,IP6,DHCP6,VPN"

244 245 246 247 248
/* Pseudo group names for 'connection show <con>' */
/* e.g.: nmcli -f profile con show my-eth0 */
/* e.g.: nmcli -f active con show my-eth0 */
#define CON_SHOW_DETAIL_GROUP_PROFILE "profile"
#define CON_SHOW_DETAIL_GROUP_ACTIVE  "active"
249

250 251 252 253 254 255
typedef struct {
	NmCli *nmc;
	int argc;
	char **argv;
} ArgsInfo;

256 257
/* glib main loop variable - defined in nmcli.c */
extern GMainLoop *loop;
258 259

static ArgsInfo args_info;
260
static guint progress_id = 0;  /* ID of event source for displaying progress */
261

262
/* for readline TAB completion in editor */
263 264
typedef struct {
	NmCli *nmc;
265
	char *con_type;
266 267 268 269
	NMConnection *connection;
	NMSetting *setting;
} TabCompletionInfo;
static TabCompletionInfo nmc_tab_completion = {NULL, NULL, NULL, NULL};
270

271 272 273
/* Global variable defined in nmcli.c - used for TAB completion */
extern NmCli nm_cli;

274
static char *gen_connection_types (const char *text, int state);
275

276 277 278 279
static void
usage (void)
{
	fprintf (stderr,
280 281
	         _("Usage: nmcli connection { COMMAND | help }\n\n"
	           "COMMAND := { show | up | down | add | modify | edit | delete | reload | load }\n\n"
282
	           "  show [--active] [[id | uuid | path | apath] <ID>] ...\n\n"
283
#if WITH_WIMAX
284
	           "  up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>] [nsp <name>]\n\n"
285
#else
286
	           "  up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>]\n\n"
287
#endif
288 289
	           "  down [id | uuid | path | apath] <ID>\n\n"
	           "  add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n"
290
	           "  modify [--temporary] [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n\n"
291 292 293 294 295 296 297 298 299 300 301 302 303
	           "  edit [id | uuid | path] <ID>\n"
	           "  edit [type <new_con_type>] [con-name <new_con_name>]\n\n"
	           "  delete [id | uuid | path] <ID>\n\n"
	           "  reload\n\n"
	           "  load <filename> [ <filename>... ]\n\n"));
}

static void
usage_connection_show (void)
{
	fprintf (stderr,
	         _("Usage: nmcli connection show { ARGUMENTS | help }\n"
	           "\n"
304
	           "ARGUMENTS := [--active]\n"
305
	           "\n"
306 307 308 309
	           "List in-memory and on-disk connection profiles, some of which may also be\n"
	           "active if a device is using that connection profile. Without a parameter, all\n"
	           "profiles are listed. When --active option is specified, only the active\n"
	           "profiles are shown.\n"
310
	           "\n"
311
	           "ARGUMENTS := [--active] [id | uuid | path | apath] <ID> ...\n"
312
	           "\n"
313 314 315 316 317
	           "Show details for specified connections. By default, both static configuration\n"
	           "and active connection data are displayed. It is possible to filter the output\n"
	           "using global '--fields' option. Refer to the manual page for more information.\n"
	           "When --active option is specified, only the active profiles are taken into\n"
	           "account.\n"));
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
}

static void
usage_connection_up (void)
{
	fprintf (stderr,
	         _("Usage: nmcli connection up { ARGUMENTS | help }\n"
	           "\n"
	           "ARGUMENTS := [id | uuid | path] <ID> [ifname <ifname>] [ap <BSSID>] [nsp <name>]\n"
	           "\n"
	           "Activate a connection on a device. The profile to activate is identified by its\n"
	           "name, UUID or D-Bus path.\n"
	           "\n"
	           "ARGUMENTS := ifname <ifname> [ap <BSSID>] [nsp <name>]\n"
	           "\n"
	           "Activate a device with a connection. The connection profile is selected\n"
	           "automatically by NetworkManager.\n"
	           "\n"
	           "ifname - specifies the device to active the connection on\n"
	           "ap     - specifies AP to connect to (only valid for Wi-Fi)\n"
	           "nsp    - specifies NSP to connect to (only valid for WiMAX)\n\n"));
}

static void
usage_connection_down (void)
{
	fprintf (stderr,
	         _("Usage: nmcli connection down { ARGUMENTS | help }\n"
	           "\n"
	           "ARGUMENTS := [id | uuid | path | apath] <ID>\n"
	           "\n"
	           "Deactivate a connection from a device (without preventing the device from\n"
	           "further auto-activation). The profile to deactivate is identified by its name,\n"
	           "UUID or D-Bus path.\n\n"));
352 353 354 355 356 357
}

static void
usage_connection_add (void)
{
	fprintf (stderr,
358 359 360 361 362 363 364 365
	         _("Usage: nmcli connection add { ARGUMENTS | help }\n"
	           "\n"
	           "ARGUMENTS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n"
	           "  COMMON_OPTIONS:\n"
	           "                  type <type>\n"
	           "                  ifname <interface name> | \"*\"\n"
	           "                  [con-name <connection name>]\n"
	           "                  [autoconnect yes|no]\n\n"
366
	           "                  [save yes|no]\n\n"
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
	           "  TYPE_SPECIFIC_OPTIONS:\n"
	           "    ethernet:     [mac <MAC address>]\n"
	           "                  [cloned-mac <cloned MAC address>]\n"
	           "                  [mtu <MTU>]\n\n"
	           "    wifi:         ssid <SSID>\n"
	           "                  [mac <MAC address>]\n"
	           "                  [cloned-mac <cloned MAC address>]\n"
	           "                  [mtu <MTU>]\n\n"
	           "    wimax:        [mac <MAC address>]\n"
	           "                  [nsp <NSP>]\n\n"
	           "    pppoe:        username <PPPoE username>\n"
	           "                  [password <PPPoE password>]\n"
	           "                  [service <PPPoE service name>]\n"
	           "                  [mtu <MTU>]\n"
	           "                  [mac <MAC address>]\n\n"
	           "    gsm:          apn <APN>\n"
	           "                  [user <username>]\n"
	           "                  [password <password>]\n\n"
	           "    cdma:         [user <username>]\n"
	           "                  [password <password>]\n\n"
	           "    infiniband:   [mac <MAC address>]\n"
	           "                  [mtu <MTU>]\n"
	           "                  [transport-mode datagram | connected]\n"
	           "                  [parent <ifname>]\n"
	           "                  [p-key <IPoIB P_Key>]\n\n"
	           "    bluetooth:    [addr <bluetooth address>]\n"
	           "                  [bt-type panu|dun-gsm|dun-cdma]\n\n"
	           "    vlan:         dev <parent device (connection  UUID, ifname, or MAC)>\n"
	           "                  id <VLAN ID>\n"
	           "                  [flags <VLAN flags>]\n"
	           "                  [ingress <ingress priority mapping>]\n"
	           "                  [egress <egress priority mapping>]\n"
	           "                  [mtu <MTU>]\n\n"
	           "    bond:         [mode balance-rr (0) | active-backup (1) | balance-xor (2) | broadcast (3) |\n"
	           "                        802.3ad    (4) | balance-tlb   (5) | balance-alb (6)]\n"
	           "                  [primary <ifname>]\n"
	           "                  [miimon <num>]\n"
	           "                  [downdelay <num>]\n"
	           "                  [updelay <num>]\n"
	           "                  [arp-interval <num>]\n"
	           "                  [arp-ip-target <num>]\n\n"
408
	           "    bond-slave:   master <master (ifname, or connection UUID or name)>\n\n"
409
	           "    team:         [config <file>|<raw JSON data>]\n\n"
410
	           "    team-slave:   master <master (ifname, or connection UUID or name)>\n"
411 412 413 414 415 416
	           "                  [config <file>|<raw JSON data>]\n\n"
	           "    bridge:       [stp yes|no]\n"
	           "                  [priority <num>]\n"
	           "                  [forward-delay <2-30>]\n"
	           "                  [hello-time <1-10>]\n"
	           "                  [max-age <6-40>]\n"
417 418
	           "                  [ageing-time <0-1000000>]\n"
	           "                  [mac <MAC address>]\n\n"
419
	           "    bridge-slave: master <master (ifname, or connection UUID or name)>\n"
420 421 422
	           "                  [priority <0-63>]\n"
	           "                  [path-cost <1-65535>]\n"
	           "                  [hairpin yes|no]\n\n"
423
	           "    vpn:          vpn-type vpnc|openvpn|pptp|openconnect|openswan|libreswan|ssh|l2tp|iodine|...\n"
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
	           "                  [user <username>]\n\n"
	           "    olpc-mesh:    ssid <SSID>\n"
	           "                  [channel <1-13>]\n"
	           "                  [dhcp-anycast <MAC address>]\n\n"
	           "  IP_OPTIONS:\n"
	           "                  [ip4 <IPv4 address>] [gw4 <IPv4 gateway>]\n"
	           "                  [ip6 <IPv6 address>] [gw6 <IPv6 gateway>]\n\n"));
}

static void
usage_connection_modify (void)
{
	fprintf (stderr,
	         _("Usage: nmcli connection modify { ARGUMENTS | help }\n"
	           "\n"
439
	           "ARGUMENTS := [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n"
440
	           "\n"
441
	           "Modify one or more properties of the connection profile.\n"
442
	           "The profile is identified by its name, UUID or D-Bus path. For multi-valued\n"
443
	           "properties you can use optional '+' or '-' prefix to the property name.\n"
444
	           "The '+' sign allows appending items instead of overwriting the whole value.\n"
445
	           "The '-' sign allows removing selected items instead of the whole value.\n"
446 447
	           "\n"
	           "Examples:\n"
448 449
	           "nmcli con mod home-wifi wifi.ssid rakosnicek\n"
	           "nmcli con mod em1-1 ipv4.method manual ipv4.addr \"192.168.1.2/24, 10.10.1.5/8\"\n"
450 451
	           "nmcli con mod em1-1 +ipv4.dns 8.8.4.4\n"
	           "nmcli con mod em1-1 -ipv4.dns 1\n"
452
	           "nmcli con mod em1-1 -ipv6.addr \"abbe::cafe/56\"\n"
453 454
	           "nmcli con mod bond0 +bond.options mii=500\n"
	           "nmcli con mod bond0 -bond.options downdelay\n\n"));
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
}

static void
usage_connection_edit (void)
{
	fprintf (stderr,
	         _("Usage: nmcli connection edit { ARGUMENTS | help }\n"
	           "\n"
	           "ARGUMENTS := [id | uuid | path] <ID>\n"
	           "\n"
	           "Edit an existing connection profile in an interactive editor.\n"
	           "The profile is identified by its name, UUID or D-Bus path\n"
	           "\n"
	           "ARGUMENTS := [type <new connection type>] [con-name <new connection name>]\n"
	           "\n"
	           "Add a new connection profile in an interactive editor.\n\n"));
}

static void
usage_connection_delete (void)
{
	fprintf (stderr,
	         _("Usage: nmcli connection delete { ARGUMENTS | help }\n"
	           "\n"
	           "ARGUMENTS := [id | uuid | path] <ID>\n"
	           "\n"
	           "Delete a connection profile.\n"
	           "The profile is identified by its name, UUID or D-Bus path.\n\n"));
}

static void
usage_connection_reload (void)
{
	fprintf (stderr,
	         _("Usage: nmcli connection reload { help }\n"
	           "\n"
	           "Reload all connection files from disk.\n\n"));
}

static void
usage_connection_load (void)
{
	fprintf (stderr,
	         _("Usage: nmcli connection load { ARGUMENTS | help }\n"
	           "\n"
	           "ARGUMENTS := <filename> [<filename>...]\n"
	           "\n"
	           "Load/reload one or more connection files from disk. Use this after manually\n"
	           "editing a connection file to ensure that NetworkManager is aware of its latest\n"
	           "state.\n\n"));
505 506
}

507

508 509
/* The real commands that do something - i.e. not 'help', etc. */
static const char *real_con_commands[] = {
510
	"show",
511 512
	"up",
	"down",
513
	"add",
514
	"modify",
515
	"edit",
516
	"delete",
517
	"reload",
518
	"load",
519 520 521
	NULL
};

522 523 524 525
/* quit main loop */
static void
quit (void)
{
526 527
	if (progress_id) {
		g_source_remove (progress_id);
528
		progress_id = 0;
529 530 531
		nmc_terminal_erase_line ();
	}

532 533 534
	g_main_loop_quit (loop);  /* quit main loop */
}

535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
static const char *
construct_header_name (const char *base, const char *spec)
{
	static char header_name[128];

	if (spec == NULL)
		return base;

	g_strlcpy (header_name, base, sizeof (header_name));
	g_strlcat (header_name, " (", sizeof (header_name));
	g_strlcat (header_name, spec, sizeof (header_name));
	g_strlcat (header_name, ")", sizeof (header_name));

	return header_name;
}

static const char *
active_connection_state_to_string (NMActiveConnectionState state)
{
	switch (state) {
	case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
		return _("activating");
	case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
		return _("activated");
	case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING:
		return _("deactivating");
	case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED:
		return _("deactivated");
	case NM_ACTIVE_CONNECTION_STATE_UNKNOWN:
	default:
		return _("unknown");
	}
}

static const char *
vpn_connection_state_to_string (NMVPNConnectionState state)
{
	switch (state) {
	case NM_VPN_CONNECTION_STATE_PREPARE:
		return _("VPN connecting (prepare)");
	case NM_VPN_CONNECTION_STATE_NEED_AUTH:
		return _("VPN connecting (need authentication)");
	case NM_VPN_CONNECTION_STATE_CONNECT:
		return _("VPN connecting");
	case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
		return _("VPN connecting (getting IP configuration)");
	case NM_VPN_CONNECTION_STATE_ACTIVATED:
		return _("VPN connected");
	case NM_VPN_CONNECTION_STATE_FAILED:
		return _("VPN connection failed");
	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
		return _("VPN disconnected");
	default:
		return _("unknown");
	}
}

/* Caller has to free the returned string */
static char *
get_ac_device_string (NMActiveConnection *active)
{
	GString *dev_str;
	const GPtrArray *devices;
	int i;

	if (!active)
		return NULL;

	/* Get devices of the active connection */
	dev_str = g_string_new (NULL);
	devices = nm_active_connection_get_devices (active);
	for (i = 0; devices && (i < devices->len); i++) {
		NMDevice *device = g_ptr_array_index (devices, i);
		const char *dev_iface = nm_device_get_iface (device);

		if (dev_iface) {
			g_string_append (dev_str, dev_iface);
			g_string_append_c (dev_str, ',');
		}
	}
	if (dev_str->len > 0)
		g_string_truncate (dev_str, dev_str->len - 1);  /* Cut off last ',' */

	return g_string_free (dev_str, FALSE);
}

static NMActiveConnection *
get_ac_for_connection (const GPtrArray *active_cons, NMConnection *connection)
{
	const char *con_path;
	int i;
	NMActiveConnection *ac = NULL;

	/* Is the connection active? */
	con_path = nm_connection_get_path (connection);
	for (i = 0; active_cons && i < active_cons->len; i++) {
		NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);

		if (!g_strcmp0 (nm_active_connection_get_connection (candidate), con_path)) {
			ac = candidate;
			break;
		}
	}
	return ac;
}

static NMConnection *
get_connection_for_active (const GSList *con_list, NMActiveConnection *active)
{
	const GSList *iter;
	const char *path;

	path = nm_active_connection_get_connection (active);
	g_return_val_if_fail (path != NULL, NULL);

	for (iter = con_list; iter; iter = g_slist_next (iter)) {
		NMConnection *candidate = NM_CONNECTION (iter->data);

		if (strcmp (nm_connection_get_path (candidate), path) == 0)
			return candidate;
	}
	return NULL;
}

659
static gboolean
660
nmc_connection_profile_details (NMConnection *connection, NmCli *nmc)
661 662 663
{
	GError *error = NULL;
	GArray *print_settings_array;
664
	GPtrArray *prop_array = NULL;
665 666 667 668
	int i;
	char *fields_str;
	char *fields_all =    NMC_FIELDS_SETTINGS_NAMES_ALL;
	char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL;
669
	const char *base_hdr = _("Connection profile details");
670 671 672 673 674 675 676 677 678
	gboolean was_output = FALSE;

	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
		fields_str = fields_common;
	else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
		fields_str = fields_all;
	else
		fields_str = nmc->required_fields;

679
	print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, TRUE, &prop_array, &error);
680
	if (error) {
681
		g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message);
682 683 684 685
		g_error_free (error);
		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
		return FALSE;
	}
686
	g_assert (print_settings_array);
687

688
	/* Main header */
689
	nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_connection_get_id (connection));
690
	nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL,
691
	                                                 nmc_fields_settings_names, FALSE, NULL, NULL);
692 693 694

	nmc_fields_settings_names[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
	print_required_fields (nmc, nmc_fields_settings_names);
695 696 697

	/* Loop through the required settings and print them. */
	for (i = 0; i < print_settings_array->len; i++) {
698
		NMSetting *setting;
699
		int section_idx = g_array_index (print_settings_array, int, i);
700
		const char *prop_name = (const char *) g_ptr_array_index (prop_array, i);
701 702 703 704 705 706

		if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
			printf ("\n"); /* Empty line */

		was_output = FALSE;

707 708 709
		/* Remove any previous data */
		nmc_empty_output_fields (nmc);

710 711
		setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name);
		if (setting) {
712
			setting_details (setting, nmc, prop_name);
713 714
			was_output = TRUE;
			continue;
Thomas Graf's avatar
Thomas Graf committed
715
		}
716 717
	}

718
	g_array_free (print_settings_array, TRUE);
719 720
	if (prop_array)
		g_ptr_array_free (prop_array, TRUE);
721

722
	return TRUE;
723 724
}

725
static NMActiveConnection *
726 727 728 729 730
find_active_connection (const GPtrArray *active_cons,
                        const GSList *cons,
                        const char *filter_type,
                        const char *filter_val,
                        int *idx)
731
{
732
	int i;
733
	int start = (idx && *idx > 0) ? *idx : 0;
734 735 736 737
	const char *path, *a_path, *path_num, *a_path_num;
	const char *id;
	const char *uuid;
	NMConnection *con;
738
	NMActiveConnection *found = NULL;
739

740
	for (i = start; active_cons && (i < active_cons->len); i++) {
741
		NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
742

743 744 745 746 747
		path = nm_active_connection_get_connection (candidate);
		a_path = nm_object_get_path (NM_OBJECT (candidate));
		uuid = nm_active_connection_get_uuid (candidate);
		path_num = path ? strrchr (path, '/') + 1 : NULL;
		a_path_num = a_path ? strrchr (a_path, '/') + 1 : NULL;
748

749 750
		con = get_connection_for_active (cons, candidate);
		id = nm_connection_get_id (con);
751

752 753 754 755 756 757 758 759 760 761 762 763
		/* When filter_type is NULL, compare connection ID (filter_val)
		 * against all types. Otherwise, only compare against the specific
		 * type. If 'path' or 'apath' filter types are specified, comparison
		 * against numeric index (in addition to the whole path) is allowed.
		 */
		if (   (   (!filter_type || strcmp (filter_type, "id")  == 0)
		        && strcmp (filter_val, id) == 0)
		    || (   (!filter_type || strcmp (filter_type, "uuid") == 0)
		        && strcmp (filter_val, uuid) == 0)
		    || (   (!filter_type || strcmp (filter_type, "path") == 0)
		        && (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)))
		    || (   (!filter_type || strcmp (filter_type, "apath") == 0)
764 765 766 767 768 769 770 771 772
		        && (g_strcmp0 (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0)))) {
			if (!idx)
				return candidate;
			if (found) {
				*idx = i;
				return found;
			}
			found = candidate;
		}
773
	}
774 775 776 777

	if (idx)
		*idx = 0;
	return found;
778 779
}

780 781
static void
fill_output_connection (gpointer data, gpointer user_data, gboolean active_only)
782
{
783 784 785 786 787 788 789 790 791 792 793 794
	NMConnection *connection = (NMConnection *) data;
	NmCli *nmc = (NmCli *) user_data;
	NMSettingConnection *s_con;
	guint64 timestamp;
	time_t timestamp_real;
	char *timestamp_str;
	char *timestamp_real_str = "";
	NmcOutputField *arr;
	NMActiveConnection *ac = NULL;
	const char *ac_path = NULL;
	const char *ac_state = NULL;
	char *ac_dev = NULL;
795

796 797
	s_con = nm_connection_get_setting_connection (connection);
	g_assert (s_con);
798

799 800 801
	ac = get_ac_for_connection (nm_client_get_active_connections (nmc->client), connection);
	if (active_only && !ac)
		return;
802

803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
	if (ac) {
		ac_path = nm_object_get_path (NM_OBJECT (ac));
		ac_state = active_connection_state_to_string (nm_active_connection_get_state (ac));
		ac_dev = get_ac_device_string (ac);
	}

	/* Obtain field values */
	timestamp = nm_setting_connection_get_timestamp (s_con);
	timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
	if (timestamp) {
		timestamp_real = timestamp;
		timestamp_real_str = g_malloc0 (64);
		strftime (timestamp_real_str, 64, "%c", localtime (&timestamp_real));
	}

	arr = nmc_dup_fields_array (nmc_fields_con_show,
	                            sizeof (nmc_fields_con_show),
	                            0);
	set_val_strc (arr, 0, nm_setting_connection_get_id (s_con));
	set_val_strc (arr, 1, nm_setting_connection_get_uuid (s_con));
	set_val_strc (arr, 2, nm_setting_connection_get_connection_type (s_con));
	set_val_str  (arr, 3, timestamp_str);
	set_val_str  (arr, 4, timestamp ? timestamp_real_str : g_strdup (_("never")));
	set_val_strc (arr, 5, nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no"));
	set_val_strc (arr, 6, nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no"));
	set_val_strc (arr, 7, nm_connection_get_path (connection));
	set_val_strc (arr, 8, ac ? _("yes") : _("no"));
	set_val_str  (arr, 9, ac_dev);
	set_val_strc (arr, 10, ac_state);
	set_val_strc (arr, 11, ac_path);
833

834
	g_ptr_array_add (nmc->output_data, arr);
835 836
}

837 838 839 840 841
static void
fill_output_active_connection (NMActiveConnection *active,
                               NmCli *nmc,
                               gboolean with_group,
                               guint32 o_flags)
842 843
{
	GSList *iter;
844 845 846 847
	const char *active_path;
	NMSettingConnection *s_con;
	const GPtrArray *devices;
	GString *dev_str;
848
	NMActiveConnectionState state;
849
	int i;
850 851 852 853
	GSList *con_list = nmc->system_connections;
	NmcOutputField *tmpl, *arr;
	size_t tmpl_len;
	int idx_start = with_group ? 0 : 1;
854 855

	active_path = nm_active_connection_get_connection (active);
856
	state = nm_active_connection_get_state (active);
857

858
	/* Get devices of the active connection */
859
	dev_str = g_string_new (NULL);
860 861 862
	devices = nm_active_connection_get_devices (active);
	for (i = 0; devices && (i < devices->len); i++) {
		NMDevice *device = g_ptr_array_index (devices, i);
863
		const char *dev_iface = nm_device_get_iface (device);
864

865 866 867 868
		if (dev_iface) {
			g_string_append (dev_str, dev_iface);
			g_string_append_c (dev_str, ',');
		}
869 870 871 872
	}
	if (dev_str->len > 0)
		g_string_truncate (dev_str, dev_str->len - 1);  /* Cut off last ',' */

873 874
	tmpl = nmc_fields_con_active_details_general;
	tmpl_len = sizeof (nmc_fields_con_active_details_general);
875 876 877 878 879
	if (!with_group) {
		tmpl++;
		tmpl_len -= sizeof (NmcOutputField);
	}

880
	/* Fill field values */
881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
	arr = nmc_dup_fields_array (tmpl, tmpl_len, o_flags);
	if (with_group)
		set_val_strc (arr, 0, nmc_fields_con_active_details_groups[0].name);
	set_val_strc (arr, 1-idx_start, _("N/A"));
	set_val_strc (arr, 2-idx_start, nm_active_connection_get_uuid (active));
	set_val_str  (arr, 3-idx_start, dev_str->str);
	set_val_strc (arr, 4-idx_start, active_connection_state_to_string (state));
	set_val_strc (arr, 5-idx_start, nm_active_connection_get_default (active) ? _("yes") : _("no"));
	set_val_strc (arr, 6-idx_start, nm_active_connection_get_default6 (active) ? _("yes") : _("no"));
	set_val_strc (arr, 7-idx_start, nm_active_connection_get_specific_object (active));
	set_val_strc (arr, 8-idx_start, NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no"));
	set_val_strc (arr, 9-idx_start, nm_object_get_path (NM_OBJECT (active)));
	set_val_strc (arr, 10-idx_start, nm_active_connection_get_connection (active));
	set_val_strc (arr, 11-idx_start, _("N/A"));
	set_val_strc (arr, 12-idx_start, nm_active_connection_get_master (active));
896

897 898 899 900
	for (iter = con_list; iter; iter = g_slist_next (iter)) {
		NMConnection *connection = (NMConnection *) iter->data;
		const char *con_path = nm_connection_get_path (connection);

901 902
		if (!strcmp (active_path, con_path)) {
			/* This connection is active */
903
			s_con = nm_connection_get_setting_connection (connection);
904 905
			g_assert (s_con != NULL);

906
			/* Fill field values that depend on NMConnection */
907 908
			set_val_strc (arr, 1-idx_start,  nm_setting_connection_get_id (s_con));
			set_val_strc (arr, 11-idx_start, nm_setting_connection_get_zone (s_con));
909

910
			break;
911 912
		}
	}
913
	g_ptr_array_add (nmc->output_data, arr);
914

915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
	g_string_free (dev_str, FALSE);
}

typedef struct {
	char **array;
	guint32 idx;
} FillVPNDataInfo;

static void
fill_vpn_data_item (const char *key, const char *value, gpointer user_data)
{
        FillVPNDataInfo *info = (FillVPNDataInfo *) user_data;

	info->array[info->idx++] = g_strdup_printf ("%s = %s", key, value);
}

// FIXME: The same or similar code for VPN info appears also in nm-applet (applet-dialogs.c),
// and in gnome-control-center as well. It could probably be shared somehow.
static char *
get_vpn_connection_type (NMConnection *connection)
{
	const char *type, *p;

	/* The service type is in form of "org.freedesktop.NetworkManager.vpnc".
	 * Extract end part after last dot, e.g. "vpnc"
	 */
	type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
	p = strrchr (type, '.');
	return g_strdup (p ? p + 1 : type);
}

/* VPN parameters can be found at:
 * http://git.gnome.org/browse/network-manager-openvpn/tree/src/nm-openvpn-service.h
 * http://git.gnome.org/browse/network-manager-vpnc/tree/src/nm-vpnc-service.h
 * http://git.gnome.org/browse/network-manager-pptp/tree/src/nm-pptp-service.h
 * http://git.gnome.org/browse/network-manager-openconnect/tree/src/nm-openconnect-service.h
 * http://git.gnome.org/browse/network-manager-openswan/tree/src/nm-openswan-service.h
 * See also 'properties' directory in these plugins.
 */
static const gchar *
find_vpn_gateway_key (const char *vpn_type)
{
	if (g_strcmp0 (vpn_type, "openvpn") == 0)     return "remote";
	if (g_strcmp0 (vpn_type, "vpnc") == 0)        return "IPSec gateway";
	if (g_strcmp0 (vpn_type, "pptp") == 0)        return "gateway";
	if (g_strcmp0 (vpn_type, "openconnect") == 0) return "gateway";
	if (g_strcmp0 (vpn_type, "openswan") == 0)    return "right";
962 963 964
	if (g_strcmp0 (vpn_type, "libreswan") == 0)   return "right";
	if (g_strcmp0 (vpn_type, "ssh") == 0)         return "remote";
	if (g_strcmp0 (vpn_type, "l2tp") == 0)        return "gateway";
965 966 967 968 969 970 971 972 973 974 975
	return "";
}

static const gchar *
find_vpn_username_key (const char *vpn_type)
{
	if (g_strcmp0 (vpn_type, "openvpn") == 0)     return "username";
	if (g_strcmp0 (vpn_type, "vpnc") == 0)        return "Xauth username";
	if (g_strcmp0 (vpn_type, "pptp") == 0)        return "user";
	if (g_strcmp0 (vpn_type, "openconnect") == 0) return "username";
	if (g_strcmp0 (vpn_type, "openswan") == 0)    return "leftxauthusername";
976 977
	if (g_strcmp0 (vpn_type, "libreswan") == 0)   return "leftxauthusername";
	if (g_strcmp0 (vpn_type, "l2tp") == 0)        return "user";
978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
	return "";
}

enum VpnDataItem {
	VPN_DATA_ITEM_GATEWAY,
	VPN_DATA_ITEM_USERNAME
};

static const gchar *
get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item)
{
	const char *key;
	char *type = get_vpn_connection_type (connection);

	switch (vpn_data_item) {
	case VPN_DATA_ITEM_GATEWAY:
		key = find_vpn_gateway_key (type);
		break;
	case VPN_DATA_ITEM_USERNAME:
		key = find_vpn_username_key (type);
		break;
	default:
		key = "";
		break;
	}
	g_free (type);

	return nm_setting_vpn_get_data_item (nm_connection_get_setting_vpn (connection), key);
}
/* FIXME end */

static gboolean
1010
nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
1011
{
1012
	GError *error = NULL;
1013
	GArray *print_groups;
1014
	GPtrArray *group_fields = NULL;
1015
	int i;
1016
	char *fields_str;
1017 1018
	char *fields_all =    NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
	char *fields_common = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
1019 1020
	NmcOutputField *tmpl, *arr;
	size_t tmpl_len;
1021
	const char *base_hdr = _("Activate connection details");
1022
	gboolean was_output = FALSE;
1023

1024 1025 1026 1027
	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
		fields_str = fields_common;
	else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
		fields_str = fields_all;
1028
	else
1029 1030
		fields_str = nmc->required_fields;

1031
	print_groups = parse_output_fields (fields_str, nmc_fields_con_active_details_groups, TRUE, &group_fields, &error);
1032
	if (error) {
1033
		g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message);
1034
		g_error_free (error);
1035
		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1036 1037
		return FALSE;
	}
1038
	g_assert (print_groups);
1039

1040
	/* Main header */
1041
	nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_active_connection_get_uuid (acon));
1042
	nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_ALL,
1043
	                                                 nmc_fields_con_active_details_groups, FALSE, NULL, NULL);
1044 1045 1046

	nmc_fields_con_active_details_groups[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
	print_required_fields (nmc, nmc_fields_con_active_details_groups);
1047 1048 1049 1050

	/* Loop through the groups and print them. */
	for (i = 0; i < print_groups->len; i++) {
		int group_idx = g_array_index (print_groups, int, i);
1051
		char *group_fld = (char *) g_ptr_array_index (group_fields, i);
1052 1053 1054 1055 1056 1057

		if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
			printf ("\n"); /* Empty line */

		was_output = FALSE;

1058 1059 1060
		/* Remove any previous data */
		nmc_empty_output_fields (nmc);

1061
		/* GENERAL */
1062
		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[0].name) == 0) {
1063
			/* Add field names */
1064 1065
			tmpl = nmc_fields_con_active_details_general;
			tmpl_len = sizeof (nmc_fields_con_active_details_general);
1066 1067
			nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL,
			                                                 tmpl, FALSE, NULL, NULL);
1068 1069
			arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
			g_ptr_array_add (nmc->output_data, arr);
1070 1071

			/* Fill in values */
1072
			fill_output_active_connection (acon, nmc, TRUE, NMC_OF_FLAG_SECTION_PREFIX);
1073

1074
			print_data (nmc);  /* Print all data */
1075 1076 1077 1078

			was_output = TRUE;
		}

1079
		/* IP4 */
1080
		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[1].name) == 0) {
1081 1082
			gboolean b1 = FALSE;
			NMIP4Config *cfg4 = nm_active_connection_get_ip4_config (acon);
1083

1084 1085
			b1 = print_ip4_config (cfg4, nmc, "IP4", group_fld);
			was_output = was_output || b1;
1086 1087 1088 1089
		}

		/* DHCP4 */
		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[2].name) == 0) {
1090 1091
			gboolean b1 = FALSE;
			NMDHCP4Config *dhcp4 = nm_active_connection_get_dhcp4_config (acon);
1092

1093 1094
			b1 = print_dhcp4_config (dhcp4, nmc, "DHCP4", group_fld);
			was_output = was_output || b1;
1095 1096 1097 1098
		}

		/* IP6 */
		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[3].name) == 0) {
1099 1100
			gboolean b1 = FALSE;
			NMIP6Config *cfg6 = nm_active_connection_get_ip6_config (acon);
1101

1102 1103
			b1 = print_ip6_config (cfg6, nmc, "IP6", group_fld);
			was_output = was_output || b1;
1104 1105 1106 1107
		}

		/* DHCP6 */
		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[4].name) == 0) {
1108 1109
			gboolean b1 = FALSE;
			NMDHCP6Config *dhcp6 = nm_active_connection_get_dhcp6_config (acon);
1110

1111