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

#include "utils.h"
37
#include "common.h"
38
#include "settings.h"
39 40
#include "connections.h"

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

44 45 46 47 48
/* 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: ")

49 50 51 52 53 54
/* 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: ")
55
#define PROMPT_CONNECTION _("Connection (name, UUID, or path): ")
56 57 58 59 60

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

61
/* Available fields for 'connection show' */
62
static NmcOutputField nmc_fields_con_show[] = {
63 64 65 66 67 68 69 70
	{"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 */
71 72 73 74
	{"ACTIVE",          N_("ACTIVE"),         10},  /* 8 */
	{"DEVICE",          N_("DEVICE"),         10},  /* 9 */
	{"STATE",           N_("STATE"),          12},  /* 10 */
	{"ACTIVE-PATH",     N_("ACTIVE-PATH"),    51},  /* 11 */
75
	{NULL,              NULL,                  0}
76
};
77 78 79
#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"
80

81
/* Helper macro to define fields */
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
#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[];
110

111
/* Available settings for 'connection show <con>' - profile part */
112
static NmcOutputField nmc_fields_settings_names[] = {
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
	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}
139
};
140
#define NMC_FIELDS_SETTINGS_NAMES_ALL_X  NM_SETTING_CONNECTION_SETTING_NAME","\
141 142 143 144 145 146 147 148 149
                                         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","\
150
                                         NM_SETTING_ADSL_SETTING_NAME","\
151 152 153 154
                                         NM_SETTING_GSM_SETTING_NAME","\
                                         NM_SETTING_CDMA_SETTING_NAME","\
                                         NM_SETTING_BLUETOOTH_SETTING_NAME","\
                                         NM_SETTING_OLPC_MESH_SETTING_NAME","\
155
                                         NM_SETTING_VPN_SETTING_NAME","\
Jiří Klimeš's avatar
Jiří Klimeš committed
156
                                         NM_SETTING_INFINIBAND_SETTING_NAME","\
Jiří Klimeš's avatar
Jiří Klimeš committed
157
                                         NM_SETTING_BOND_SETTING_NAME","\
Thomas Graf's avatar
Thomas Graf committed
158 159
                                         NM_SETTING_VLAN_SETTING_NAME","\
                                         NM_SETTING_BRIDGE_SETTING_NAME","\
Jiri Pirko's avatar
Jiri Pirko committed
160 161
                                         NM_SETTING_BRIDGE_PORT_SETTING_NAME","\
                                         NM_SETTING_TEAM_SETTING_NAME","\
162 163
                                         NM_SETTING_TEAM_PORT_SETTING_NAME"," \
                                         NM_SETTING_DCB_SETTING_NAME
164 165
#if WITH_WIMAX
#define NMC_FIELDS_SETTINGS_NAMES_ALL    NMC_FIELDS_SETTINGS_NAMES_ALL_X","\
166
                                         NM_SETTING_WIMAX_SETTING_NAME
167 168 169
#else
#define NMC_FIELDS_SETTINGS_NAMES_ALL    NMC_FIELDS_SETTINGS_NAMES_ALL_X
#endif
170

171 172 173 174
/* Active connection data */
/* Available fields for GENERAL group */
static NmcOutputField nmc_fields_con_active_details_general[] = {
	{"GROUP",         N_("GROUP"),         9},  /* 0 */
175 176 177 178 179 180 181 182 183 184 185 186 187
	{"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}
188
};
189 190
#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"
191 192 193 194

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

/* Available fields for VPN group */
195
static NmcOutputField nmc_fields_con_active_details_vpn[] = {
196 197 198 199 200 201 202 203
	{"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}
204
};
205
#define NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL  "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG"
206

207 208 209 210 211 212
/* 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[];

213
/* Available fields for 'connection show <con>' - active part */
214
static NmcOutputField nmc_fields_con_active_details_groups[] = {
215 216 217 218 219 220
	{"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 */
221 222 223 224
	{NULL, NULL, 0, NULL}
};
#define NMC_FIELDS_CON_ACTIVE_DETAILS_ALL  "GENERAL,IP4,DHCP4,IP6,DHCP6,VPN"

225 226 227 228 229
/* 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"
230

231 232 233 234 235 236
typedef struct {
	NmCli *nmc;
	int argc;
	char **argv;
} ArgsInfo;

237 238
/* glib main loop variable - defined in nmcli.c */
extern GMainLoop *loop;
239 240

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

243
/* for readline TAB completion in editor */
244 245
typedef struct {
	NmCli *nmc;
246
	char *con_type;
247 248 249 250
	NMConnection *connection;
	NMSetting *setting;
} TabCompletionInfo;
static TabCompletionInfo nmc_tab_completion = {NULL, NULL, NULL, NULL};
251

252 253 254
/* Global variable defined in nmcli.c - used for TAB completion */
extern NmCli nm_cli;

255
static char *gen_connection_types (const char *text, int state);
256

257 258 259 260
static void
usage (void)
{
	fprintf (stderr,
261 262
	         _("Usage: nmcli connection { COMMAND | help }\n\n"
	           "COMMAND := { show | up | down | add | modify | edit | delete | reload | load }\n\n"
263
	           "  show [--active] [[id | uuid | path | apath] <ID>] ...\n\n"
264
#if WITH_WIMAX
265
	           "  up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>] [nsp <name>]\n\n"
266
#else
267
	           "  up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>]\n\n"
268
#endif
269 270
	           "  down [id | uuid | path | apath] <ID>\n\n"
	           "  add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n"
271
	           "  modify [--temporary] [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n\n"
272 273 274 275 276 277 278 279 280 281 282 283 284
	           "  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"
285
	           "ARGUMENTS := [--active]\n"
286
	           "\n"
287 288 289 290
	           "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"
291
	           "\n"
292
	           "ARGUMENTS := [--active] [id | uuid | path | apath] <ID> ...\n"
293
	           "\n"
294 295 296 297 298
	           "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"));
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
}

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"));
333 334 335 336 337 338
}

static void
usage_connection_add (void)
{
	fprintf (stderr,
339 340 341 342 343 344 345 346
	         _("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"
347
	           "                  [save yes|no]\n\n"
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
	           "  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"
389
	           "    bond-slave:   master <master (ifname, or connection UUID or name)>\n\n"
390
	           "    team:         [config <file>|<raw JSON data>]\n\n"
391
	           "    team-slave:   master <master (ifname, or connection UUID or name)>\n"
392 393 394 395 396 397
	           "                  [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"
398 399
	           "                  [ageing-time <0-1000000>]\n"
	           "                  [mac <MAC address>]\n\n"
400
	           "    bridge-slave: master <master (ifname, or connection UUID or name)>\n"
401 402 403
	           "                  [priority <0-63>]\n"
	           "                  [path-cost <1-65535>]\n"
	           "                  [hairpin yes|no]\n\n"
404
	           "    vpn:          vpn-type vpnc|openvpn|pptp|openconnect|openswan|libreswan|ssh|l2tp|iodine|...\n"
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
	           "                  [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"
420
	           "ARGUMENTS := [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n"
421
	           "\n"
422
	           "Modify one or more properties of the connection profile.\n"
423
	           "The profile is identified by its name, UUID or D-Bus path. For multi-valued\n"
424
	           "properties you can use optional '+' or '-' prefix to the property name.\n"
425
	           "The '+' sign allows appending items instead of overwriting the whole value.\n"
426
	           "The '-' sign allows removing selected items instead of the whole value.\n"
427 428
	           "\n"
	           "Examples:\n"
429 430
	           "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"
431 432
	           "nmcli con mod em1-1 +ipv4.dns 8.8.4.4\n"
	           "nmcli con mod em1-1 -ipv4.dns 1\n"
433
	           "nmcli con mod em1-1 -ipv6.addr \"abbe::cafe/56\"\n"
434 435
	           "nmcli con mod bond0 +bond.options mii=500\n"
	           "nmcli con mod bond0 -bond.options downdelay\n\n"));
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 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
}

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"));
486 487
}

488

489 490
/* The real commands that do something - i.e. not 'help', etc. */
static const char *real_con_commands[] = {
491
	"show",
492 493
	"up",
	"down",
494
	"add",
495
	"modify",
496
	"edit",
497
	"delete",
498
	"reload",
499
	"load",
500 501 502
	NULL
};

503 504 505 506
/* quit main loop */
static void
quit (void)
{
507 508
	if (progress_id) {
		g_source_remove (progress_id);
509
		progress_id = 0;
510 511 512
		nmc_terminal_erase_line ();
	}

513 514 515
	g_main_loop_quit (loop);  /* quit main loop */
}

516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
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 *
551
vpn_connection_state_to_string (NMVpnConnectionState state)
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
{
	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;
}

640
static gboolean
641
nmc_connection_profile_details (NMConnection *connection, NmCli *nmc)
642 643 644
{
	GError *error = NULL;
	GArray *print_settings_array;
645
	GPtrArray *prop_array = NULL;
646 647 648 649
	int i;
	char *fields_str;
	char *fields_all =    NMC_FIELDS_SETTINGS_NAMES_ALL;
	char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL;
650
	const char *base_hdr = _("Connection profile details");
651 652 653 654 655 656 657 658 659
	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;

660
	print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, TRUE, &prop_array, &error);
661
	if (error) {
662
		g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message);
663 664 665 666
		g_error_free (error);
		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
		return FALSE;
	}
667
	g_assert (print_settings_array);
668

669
	/* Main header */
670
	nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_connection_get_id (connection));
671
	nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL,
672
	                                                 nmc_fields_settings_names, FALSE, NULL, NULL);
673 674 675

	nmc_fields_settings_names[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
	print_required_fields (nmc, nmc_fields_settings_names);
676 677 678

	/* Loop through the required settings and print them. */
	for (i = 0; i < print_settings_array->len; i++) {
679
		NMSetting *setting;
680
		int section_idx = g_array_index (print_settings_array, int, i);
681
		const char *prop_name = (const char *) g_ptr_array_index (prop_array, i);
682 683 684 685 686 687

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

		was_output = FALSE;

688 689 690
		/* Remove any previous data */
		nmc_empty_output_fields (nmc);

691 692
		setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name);
		if (setting) {
693
			setting_details (setting, nmc, prop_name);
694 695
			was_output = TRUE;
			continue;
Thomas Graf's avatar
Thomas Graf committed
696
		}
697 698
	}

699
	g_array_free (print_settings_array, TRUE);
700 701
	if (prop_array)
		g_ptr_array_free (prop_array, TRUE);
702

703
	return TRUE;
704 705
}

706
static NMActiveConnection *
707 708 709 710 711
find_active_connection (const GPtrArray *active_cons,
                        const GSList *cons,
                        const char *filter_type,
                        const char *filter_val,
                        int *idx)
712
{
713
	int i;
714
	int start = (idx && *idx > 0) ? *idx : 0;
715 716 717 718
	const char *path, *a_path, *path_num, *a_path_num;
	const char *id;
	const char *uuid;
	NMConnection *con;
719
	NMActiveConnection *found = NULL;
720

721
	for (i = start; active_cons && (i < active_cons->len); i++) {
722
		NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
723

724 725 726 727 728
		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;
729

730 731
		con = get_connection_for_active (cons, candidate);
		id = nm_connection_get_id (con);
732

733 734 735 736 737 738 739 740 741 742 743 744
		/* 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)
745 746 747 748 749 750 751 752 753
		        && (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;
		}
754
	}
755 756 757 758

	if (idx)
		*idx = 0;
	return found;
759 760
}

761 762
static void
fill_output_connection (gpointer data, gpointer user_data, gboolean active_only)
763
{
764 765 766 767 768 769 770 771 772 773 774 775
	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;
776

777 778
	s_con = nm_connection_get_setting_connection (connection);
	g_assert (s_con);
779

780 781 782
	ac = get_ac_for_connection (nm_client_get_active_connections (nmc->client), connection);
	if (active_only && !ac)
		return;
783

784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
	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);
814

815
	g_ptr_array_add (nmc->output_data, arr);
816 817
}

818 819 820 821 822
static void
fill_output_active_connection (NMActiveConnection *active,
                               NmCli *nmc,
                               gboolean with_group,
                               guint32 o_flags)
823 824
{
	GSList *iter;
825 826 827 828
	const char *active_path;
	NMSettingConnection *s_con;
	const GPtrArray *devices;
	GString *dev_str;
829
	NMActiveConnectionState state;
830
	int i;
831 832 833 834
	GSList *con_list = nmc->system_connections;
	NmcOutputField *tmpl, *arr;
	size_t tmpl_len;
	int idx_start = with_group ? 0 : 1;
835 836

	active_path = nm_active_connection_get_connection (active);
837
	state = nm_active_connection_get_state (active);
838

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

846 847 848 849
		if (dev_iface) {
			g_string_append (dev_str, dev_iface);
			g_string_append_c (dev_str, ',');
		}
850 851 852 853
	}
	if (dev_str->len > 0)
		g_string_truncate (dev_str, dev_str->len - 1);  /* Cut off last ',' */

854 855
	tmpl = nmc_fields_con_active_details_general;
	tmpl_len = sizeof (nmc_fields_con_active_details_general);
856 857 858 859 860
	if (!with_group) {
		tmpl++;
		tmpl_len -= sizeof (NmcOutputField);
	}

861
	/* Fill field values */
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876
	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));
877

878 879 880 881
	for (iter = con_list; iter; iter = g_slist_next (iter)) {
		NMConnection *connection = (NMConnection *) iter->data;
		const char *con_path = nm_connection_get_path (connection);

882 883
		if (!strcmp (active_path, con_path)) {
			/* This connection is active */
884
			s_con = nm_connection_get_setting_connection (connection);
885 886
			g_assert (s_con != NULL);

887
			/* Fill field values that depend on NMConnection */
888 889
			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));
890

891
			break;
892 893
		}
	}
894
	g_ptr_array_add (nmc->output_data, arr);
895

896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 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
	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";
943 944 945
	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";
946 947 948 949 950 951 952 953 954 955 956
	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";
957 958
	if (g_strcmp0 (vpn_type, "libreswan") == 0)   return "leftxauthusername";
	if (g_strcmp0 (vpn_type, "l2tp") == 0)        return "user";
959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990
	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
991
nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
992
{
993
	GError *error = NULL;
994
	GArray *print_groups;
995
	GPtrArray *group_fields = NULL;
996
	int i;
997
	char *fields_str;
998 999
	char *fields_all =    NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
	char *fields_common = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
1000 1001
	NmcOutputField *tmpl, *arr;
	size_t tmpl_len;
1002
	const char *base_hdr = _("Activate connection details");
1003
	gboolean was_output = FALSE;
1004

1005 1006 1007 1008
	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;
1009
	else
1010 1011
		fields_str = nmc->required_fields;

1012
	print_groups = parse_output_fields (fields_str, nmc_fields_con_active_details_groups, TRUE, &group_fields, &error);
1013
	if (error) {
1014
		g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message);
1015
		g_error_free (error);
1016
		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1017 1018
		return FALSE;
	}
1019
	g_assert (print_groups);
1020

1021
	/* Main header */
1022
	nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_active_connection_get_uuid (acon));
1023
	nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_ALL,
1024
	                                                 nmc_fields_con_active_details_groups, FALSE, NULL, NULL);
1025 1026 1027

	nmc_fields_con_active_details_groups[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
	print_required_fields (nmc, nmc_fields_con_active_details_groups);
1028 1029 1030 1031

	/* 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);
1032
		char *group_fld = (char *) g_ptr_array_index (group_fields, i);
1033 1034 1035 1036 1037 1038

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

		was_output = FALSE;

1039 1040 1041
		/* Remove any previous data */
		nmc_empty_output_fields (nmc);

1042
		/* GENERAL */
1043
		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[0].name) == 0) {
1044
			/* Add field names */
1045 1046
			tmpl = nmc_fields_con_active_details_general;
			tmpl_len = sizeof (nmc_fields_con_active_details_general);
1047 1048
			nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL,
			                                                 tmpl, FALSE, NULL, NULL);
1049 1050
			arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
			g_ptr_array_add (nmc->output_data, arr);
1051 1052

			/* Fill in values */
1053
			fill_output_active_connection (acon, nmc, TRUE, NMC_OF_FLAG_SECTION_PREFIX);
1054

1055
			print_data (nmc);  /* Print all data */
1056 1057 1058 1059

			was_output = TRUE;
		}

1060
		/* IP4 */
1061
		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[1].name) == 0) {
1062 1063
			gboolean b1 = FALSE;
			NMIP4Config *cfg4 = nm_active_connection_get_ip4_config (acon);
1064

1065 1066
			b1 = print_ip4_config (cfg4, nmc, "IP4", group_fld);
			was_output = was_output || b1;
1067 1068 1069 1070
		}

		/* DHCP4 */
		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[2].name) == 0) {
1071
			gboolean b1 = FALSE;
1072
			NMDhcp4Config *dhcp4 = nm_active_connection_get_dhcp4_config (acon);
1073

1074 1075
			b1 = print_dhcp4_config (dhcp4, nmc, "DHCP4", group_fld);
			was_output = was_output || b1;
1076 1077 1078 1079
		}

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

1083 1084
			b1 = print_ip6_config (cfg6, nmc, "IP6", group_fld);
			was_output = was_output || b1;
1085 1086 1087 1088
		}

		/* DHCP6 */
		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[4].name) == 0) {
1089
			gboolean b1 = FALSE;
1090
			NMDhcp6Config *dhcp6 = nm_active_connection_get_dhcp6_config (acon);
1091

1092 1093
			b1 = print_dhcp6_config (dhcp6, nmc, "DHCP6", group_fld);
			was_output = was_output || b1;
1094 1095 1096 1097
		}

		/* VPN */
		if (NM_IS_VPN_CONNECTION (acon) &&
1098
		    strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[5].name) == 0) {
1099 1100
			NMConnection *con;
			NMSettingConnection *s_con;
1101 1102
			NMSettingVpn *s_vpn;
			NMVpnConnectionState vpn_state;
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
			char *type_str, *banner_str, *vpn_state_str;
			const char *username = NULL;
			char **vpn_data_array = NULL;
			guint32 items_num;

			con = get_connection_for_active (nmc->system_connections, acon);

			s_con = nm_connection_get_setting_connection (con);
			g_assert (s_con != NULL);

1113 1114
			tmpl = nmc_fields_con_active_details_vpn;
			tmpl_len = sizeof (nmc_fields_con_active_details_vpn);
1115 1116
			nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL,
			                                                 tmpl, FALSE, NULL, NULL);
1117 1118
			arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
			g_ptr_array_add (nmc->output_data, arr);
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139

			s_vpn = nm_connection_get_setting_vpn (con);
			if (s_vpn) {
				items_num = nm_setting_vpn_get_num_data_items (s_vpn);
				if (items_num > 0) {
					FillVPNDataInfo info;

					vpn_data_array = g_new (char *, items_num + 1);
					info.array = vpn_data_array;
					info.idx = 0;
					nm_setting_vpn_foreach_data_item (s_vpn, &fill_vpn_data_item, &info);
					vpn_data_array[items_num] = NULL;
				}
				username = nm_setting_vpn_get_user_name (s_vpn);
			}

			type_str = get_vpn_connection_type (con);
			banner_str = g_strescape (nm_vpn_connection_get_banner (NM_VPN_CONNECTION (acon)), "");
			vpn_state = nm_vpn_connection_get_vpn_state (NM_VPN_CONNECTION (acon));
			vpn_state_str = g_strdup_printf ("%d - %s", vpn_state, vpn_connection_state_to_string (vpn_state));

1140 1141
			/* Add values */
			arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX);
1142
			set_val_strc (arr, 0, nmc_fields_con_active_details_groups[5].name);
1143 1144 1145 1146 1147 1148 1149 1150 1151
			set_val_str  (arr, 1, type_str);
			set_val_strc (arr, 2, username ? username : get_vpn_data_item (con, VPN_DATA_ITEM_USERNAME));
			set_val_strc (arr, 3, get_vpn_data_item (con, VPN_DATA_ITEM_GATEWAY));
			set_val_str  (arr, 4, banner_str);
			set_val_str  (arr, 5, vpn_state_str);
			set_val_arr  (arr, 6, vpn_data_array);
			g_ptr_array_add (nmc->output_data, arr);

			print_data (nmc);  /* Print all data */
1152 1153
			was_output = TRUE;
		}
1154 1155
	}

1156
	g_array_free (print_groups, TRUE);
1157 1158
	if (group_fields)
		g_ptr_array_free (group_fields, TRUE);