diff options
| author | Michael Biebl <biebl@debian.org> | 2016-08-26 02:18:32 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2016-08-26 02:18:32 +0200 |
| commit | 7514efc2f38c9ace4557d4e69d68e7d380389030 (patch) | |
| tree | 7fb00fda86cfcc2ca377f191633a7cfdbfea7ca3 /clients | |
| parent | d6201f5d8daada3d64a0a3e0038e14eebec683ce (diff) | |
Imported Upstream version 1.4.0 upstream/1.4.0
Diffstat (limited to 'clients')
94 files changed, 4002 insertions, 5979 deletions
diff --git a/clients/Makefile.in b/clients/Makefile.in index b9439b75..2d82a284 100644 --- a/clients/Makefile.in +++ b/clients/Makefile.in @@ -91,8 +91,9 @@ host_triplet = @host@ bin_PROGRAMS = nm-online$(EXEEXT) subdir = clients ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ - $(top_srcdir)/m4/compiler_warnings.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \ + $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_options.m4 \ $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gnome-code-coverage.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ @@ -261,6 +262,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@ DHCPCD_PATH = @DHCPCD_PATH@ DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ DLLTOOL = @DLLTOOL@ +DL_LIBS = @DL_LIBS@ DNSMASQ_PATH = @DNSMASQ_PATH@ DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@ DSYMUTIL = @DSYMUTIL@ @@ -317,13 +319,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ IPTABLES_PATH = @IPTABLES_PATH@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@ LIBAUDIT_LIBS = @LIBAUDIT_LIBS@ -LIBDL = @LIBDL@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ @@ -335,6 +338,8 @@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@ +LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@ LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ @@ -395,6 +400,8 @@ QT_CFLAGS = @QT_CFLAGS@ QT_LIBS = @QT_LIBS@ RANLIB = @RANLIB@ READLINE_LIBS = @READLINE_LIBS@ +SANITIZERS = @SANITIZERS@ +SANITIZER_ENV = @SANITIZER_ENV@ SED = @SED@ SELINUX_CFLAGS = @SELINUX_CFLAGS@ SELINUX_LIBS = @SELINUX_LIBS@ @@ -430,6 +437,7 @@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_RANLIB = @ac_ct_RANLIB@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ diff --git a/clients/cli/Makefile.in b/clients/cli/Makefile.in index 01b2433a..beb0d697 100644 --- a/clients/cli/Makefile.in +++ b/clients/cli/Makefile.in @@ -94,8 +94,9 @@ host_triplet = @host@ @BUILD_NMCLI_TRUE@@WITH_POLKIT_AGENT_TRUE@am__append_3 = $(POLKIT_LIBS) subdir = clients/cli ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ - $(top_srcdir)/m4/compiler_warnings.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \ + $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_options.m4 \ $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gnome-code-coverage.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ @@ -242,6 +243,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@ DHCPCD_PATH = @DHCPCD_PATH@ DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ DLLTOOL = @DLLTOOL@ +DL_LIBS = @DL_LIBS@ DNSMASQ_PATH = @DNSMASQ_PATH@ DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@ DSYMUTIL = @DSYMUTIL@ @@ -298,13 +300,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ IPTABLES_PATH = @IPTABLES_PATH@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@ LIBAUDIT_LIBS = @LIBAUDIT_LIBS@ -LIBDL = @LIBDL@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ @@ -316,6 +319,8 @@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@ +LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@ LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ @@ -376,6 +381,8 @@ QT_CFLAGS = @QT_CFLAGS@ QT_LIBS = @QT_LIBS@ RANLIB = @RANLIB@ READLINE_LIBS = @READLINE_LIBS@ +SANITIZERS = @SANITIZERS@ +SANITIZER_ENV = @SANITIZER_ENV@ SED = @SED@ SELINUX_CFLAGS = @SELINUX_CFLAGS@ SELINUX_LIBS = @SELINUX_LIBS@ @@ -411,6 +418,7 @@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_RANLIB = @ac_ct_RANLIB@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ diff --git a/clients/cli/agent.c b/clients/cli/agent.c index b8a56d23..bc837c72 100644 --- a/clients/cli/agent.c +++ b/clients/cli/agent.c @@ -138,6 +138,9 @@ secrets_requested (NMSecretAgentSimple *agent, static NMCResultCode do_agent_secret (NmCli *nmc, int argc, char **argv) { + if (nmc->complete) + return nmc->return_value; + /* Create secret agent */ nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-agent"); if (nmc->secret_agent) { @@ -160,6 +163,9 @@ do_agent_polkit (NmCli *nmc, int argc, char **argv) { GError *error = NULL; + if (nmc->complete) + return nmc->return_value; + /* Initialize polkit agent */ if (!nmc_polkit_agent_init (nmc, TRUE, &error)) { g_dbus_error_strip_remote_error (error); @@ -182,6 +188,9 @@ do_agent_all (NmCli *nmc, int argc, char **argv) { NMCResultCode secret_res; + if (nmc->complete) + return nmc->return_value; + /* Run both secret and polkit agent */ secret_res = do_agent_secret (nmc, argc, argv); if (secret_res != NMC_RESULT_SUCCESS) @@ -195,6 +204,14 @@ do_agent_all (NmCli *nmc, int argc, char **argv) return nmc->return_value; } +static const NMCCommand agent_cmds[] = { + {"secret", do_agent_secret, usage_agent_secret }, + {"polkit", do_agent_polkit, usage_agent_polkit }, + {"all", do_agent_all, usage_agent_all }, + {NULL, do_agent_all, usage } +}; + + NMCResultCode do_agent (NmCli *nmc, int argc, char **argv) { @@ -208,39 +225,5 @@ do_agent (NmCli *nmc, int argc, char **argv) return nmc->return_value; } - if (argc == 0) { - nmc->return_value = do_agent_all (nmc, 0, NULL); - } - - if (argc > 0) { - if (nmc_arg_is_help (*argv)) { - usage (); - goto usage_exit; - } else if (matches (*argv, "secret") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_agent_secret (); - goto usage_exit; - } - nmc->return_value = do_agent_secret (nmc, argc-1, argv+1); - } else if (matches (*argv, "polkit") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_agent_polkit (); - goto usage_exit; - } - nmc->return_value = do_agent_polkit (nmc, argc-1, argv+1); - } else if (matches (*argv, "all") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_agent_all (); - goto usage_exit; - } - nmc->return_value = do_agent_all (nmc, argc-1, argv+1); - } else { - usage (); - g_string_printf (nmc->return_text, _("Error: 'agent' command '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } - } - -usage_exit: - return nmc->return_value; + return nmc_do_cmd (nmc, agent_cmds, *argv, argc, argv); } diff --git a/clients/cli/common.c b/clients/cli/common.c index 05b76564..f1ec46a1 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -872,6 +872,7 @@ nmc_team_check_config (const char *config, char **out_config, GError **error) * @filter_val: connection to find (connection name, UUID or path) * @start: where to start in @list. The location is updated so that the function * can be called multiple times (for connections with the same name). + * @complete: print possible completions * * Find a connection in @list according to @filter_val. @filter_type determines * what property is used for comparison. When @filter_type is NULL, compare @@ -885,7 +886,8 @@ NMConnection * nmc_find_connection (const GPtrArray *connections, const char *filter_type, const char *filter_val, - int *start) + int *start, + gboolean complete) { NMConnection *connection; NMConnection *found = NULL; @@ -907,20 +909,36 @@ nmc_find_connection (const GPtrArray *connections, * type. If 'path' filter type is 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)))) { - if (!start) - return connection; - if (found) { - *start = i; - return found; - } - found = connection; + if (!filter_type || strcmp (filter_type, "id") == 0) { + if (complete) + nmc_complete_strings (filter_val, id, NULL); + if (strcmp (filter_val, id) == 0) + goto found; + } + + if (!filter_type || strcmp (filter_type, "uuid") == 0) { + if (complete && (filter_type || *filter_val)) + nmc_complete_strings (filter_val, uuid, NULL); + if (strcmp (filter_val, uuid) == 0) + goto found; + } + + if (!filter_type || strcmp (filter_type, "path") == 0) { + if (complete && (filter_type || *filter_val)) + nmc_complete_strings (filter_val, path, filter_type ? path_num : NULL, NULL); + if (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)) + goto found; + } + + continue; +found: + if (!start) + return connection; + if (found) { + *start = i; + return found; } + found = connection; } if (start) @@ -1041,7 +1059,8 @@ get_secrets_from_user (const char *request_id, } } g_print ("%s\n", msg); - pwd = nmc_readline_echo (echo_on, "%s (%s): ", secret->name, secret->prop_name); + pwd = nmc_readline_echo (secret->password ? echo_on : TRUE, + "%s (%s): ", secret->name, secret->prop_name); if (!pwd) pwd = g_strdup (""); } else { @@ -1096,7 +1115,7 @@ nmc_secrets_requested (NMSecretAgentSimple *agent, p = strrchr (path, '/'); if (p) *p = '\0'; - connection = nmc_find_connection (nmc->connections, "path", path, NULL); + connection = nmc_find_connection (nmc->connections, "path", path, NULL, FALSE); g_free (path); } @@ -1393,3 +1412,119 @@ nmc_parse_lldp_capabilities (guint value) return g_string_free (str, FALSE); } + +/** + * nmc_do_cmd: + * @nmc: Client instance + * @cmds: Command table + * @cmd: Command + * @argc: Argument count + * @argv: Arguments vector + * + * Picks the right callback to handle command from the command table. + * If --help argument follows and the usage callback is specified for the command + * it calls the usage callback. + * + * The command table is terminated with a %NULL command. The terminating + * entry's handlers are called if the command is empty. + * + * Returns: a nmcli return code + */ +NMCResultCode +nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char **argv) +{ + const NMCCommand *c; + + if (argc == 0 && nmc->complete) + return nmc->return_value; + + if (argc == 1 && nmc->complete) { + for (c = cmds; c->cmd; ++c) { + if (!*cmd || matches (cmd, c->cmd) == 0) + g_print ("%s\n", c->cmd); + } + return nmc->return_value; + } + + for (c = cmds; c->cmd; ++c) { + if (cmd && matches (cmd, c->cmd) == 0) + break; + } + + if (c->cmd) { + /* A valid command was specified. */ + if (c->usage && nmc_arg_is_help (*(argv+1))) + c->usage (); + else + nmc->return_value = c->func (nmc, argc-1, argv+1); + } else if (cmd) { + /* Not a known command. */ + if (nmc_arg_is_help (cmd) && c->usage) { + c->usage (); + } else { + g_string_printf (nmc->return_text, _("Error: argument '%s' not understood. Try passing --help instead."), cmd); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } else if (c->func) { + /* No command, run the default handler. */ + nmc->return_value = c->func (nmc, argc, argv); + } else { + /* No command and no default handler. */ + g_string_printf (nmc->return_text, _("Error: missing argument. Try passing --help.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + + return nmc->return_value; +} + +/** + * nmc_complete_strings: + * @prefix: a string to match + * @...: a %NULL-terminated list of candidate strings + * + * Prints all the matching candidates for completion. Useful when there's + * no better way to suggest completion other than a hardcoded string list. + */ +void +nmc_complete_strings (const char *prefix, ...) +{ + va_list args; + const char *candidate; + + va_start (args, prefix); + while ((candidate = va_arg (args, const char *))) { + if (!*prefix || matches (prefix, candidate) == 0) + g_print ("%s\n", candidate); + } + va_end (args); +} + +/** + * nmc_complete_bool: + * @prefix: a string to match + * @...: a %NULL-terminated list of candidate strings + * + * Prints all the matching possible boolean values for completion. + */ +void +nmc_complete_bool (const char *prefix) +{ + nmc_complete_strings (prefix, "true", "yes", "on", + "false", "no", "off", NULL); +} + +/** + * nmc_error_get_simple_message: + * @error: a GError + * + * Returns a simplified message for some errors hard to understand. + */ +const char * +nmc_error_get_simple_message (GError *error) +{ + /* Return a clear message instead of the obscure D-Bus policy error */ + if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED)) + return _("access denied"); + else + return error->message; +} diff --git a/clients/cli/common.h b/clients/cli/common.h index 26459e4b..579c44a7 100644 --- a/clients/cli/common.h +++ b/clients/cli/common.h @@ -48,7 +48,8 @@ gboolean nmc_team_check_config (const char *config, char **out_config, GError ** NMConnection *nmc_find_connection (const GPtrArray *connections, const char *filter_type, const char *filter_val, - int *start); + int *start, + gboolean complete); void nmc_secrets_requested (NMSecretAgentSimple *agent, const char *request_id, @@ -73,4 +74,23 @@ int nmc_rl_set_deftext (void); char *nmc_parse_lldp_capabilities (guint value); +typedef struct { + const char *cmd; + NMCResultCode (*func) (NmCli *nmc, int argc, char **argv); + void (*usage) (void); +} NMCCommand; + +NMCResultCode nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *argv0, int argc, char **argv); + +void nmc_complete_strings (const char *prefix, ...) G_GNUC_NULL_TERMINATED; + +void nmc_complete_bool (const char *prefix); + +const char *nmc_error_get_simple_message (GError *error); + +extern NmcOutputField nmc_fields_ip4_config[]; +extern NmcOutputField nmc_fields_dhcp4_config[]; +extern NmcOutputField nmc_fields_ip6_config[]; +extern NmcOutputField nmc_fields_dhcp6_config[]; + #endif /* NMC_COMMON_H */ diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 0e9a18ed..ea7c8a79 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -33,28 +33,89 @@ #include "common.h" #include "settings.h" #include "connections.h" +#include "devices.h" #include "nm-secret-agent-simple.h" #include "polkit-agent.h" #include "nm-vpn-helpers.h" +typedef struct _OptionInfo OptionInfo; +struct _OptionInfo { + const char *setting_name; + const char *property; + const char *option; + enum { + OPTION_NONE = 0x00, + OPTION_REQD = 0x01, /* Don't ask to ask. */ + OPTION_DONT_ASK = 0x02, /* Don't ask interactively by default */ + OPTION_MULTI = 0x04, /* Ask multiple times, do an append instead of set. */ + + OPTION_DISABLED = 0x10, /* Don't ask due to runtime decision. */ + OPTION_ENABLED = 0x20, /* Override OPTION_DONT_ASK due to runtime decision. */ + } flags; + const char *prompt; + const char *def_hint; + gboolean (*check_and_set)(NmCli *nmc, NMConnection *connection, OptionInfo *option, const char *value, GError **error); + rl_compentry_func_t *generator_func; +}; + /* 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: ") /* define some other prompts */ -#define PROMPT_CON_TYPE _("Connection type: ") -#define PROMPT_VPN_TYPE _("VPN type: ") -#define PROMPT_MASTER _("Master: ") -#define PROMPT_CONNECTION _("Connection (name, UUID, or path): ") -#define PROMPT_VPN_CONNECTION _("VPN connection (name, UUID, or path): ") -#define PROMPT_CONNECTIONS _("Connection(s) (name, UUID, or path): ") -#define PROMPT_ACTIVE_CONNECTIONS _("Connection(s) (name, UUID, path or apath): ") -#define PROMPT_IP_TUNNEL_MODE _("Tunnel mode: ") -#define PROMPT_MACVLAN_MODE _("MACVLAN mode: ") +#define PROMPT_CON_TYPE N_("Connection type") +#define PROMPT_VPN_TYPE N_("VPN type") +#define PROMPT_MASTER N_("Master") + +#define PROMPT_IB_MODE N_("Transport mode") +#define WORD_DATAGRAM "datagram" +#define WORD_CONNECTED "connected" +#define PROMPT_IB_MODE_CHOICES "(" WORD_DATAGRAM "/" WORD_CONNECTED ") [" WORD_DATAGRAM "]" + +#define PROMPT_BT_TYPE N_("Bluetooth type") +#define WORD_PANU "panu" +#define WORD_DUN_GSM "dun-gsm" +#define WORD_DUN_CDMA "dun-cdma" +#define PROMPT_BT_TYPE_CHOICES "(" WORD_PANU "/" WORD_DUN_GSM "/" WORD_DUN_CDMA ") [" WORD_PANU "]" + +#define PROMPT_BOND_MODE N_("Bonding mode") + +#define PROMPT_BOND_MON_MODE N_("Bonding monitoring mode") +#define WORD_MIIMON "miimon" +#define WORD_ARP "arp" +#define PROMPT_BOND_MON_MODE_CHOICES "(" WORD_MIIMON "/" WORD_ARP ") [" WORD_MIIMON "]" + +#define PROMPT_ADSL_PROTO N_("Protocol") +#define PROMPT_ADSL_PROTO_CHOICES "(" NM_SETTING_ADSL_PROTOCOL_PPPOA "/" NM_SETTING_ADSL_PROTOCOL_PPPOE "/" NM_SETTING_ADSL_PROTOCOL_IPOATM ")" + +#define PROMPT_WIFI_MODE N_("Wi-Fi mode") +#define WORD_INFRA "infrastructure" +#define WORD_AP "ap" +#define WORD_ADHOC "adhoc" +#define PROMPT_WIFI_MODE_CHOICES "(" WORD_INFRA "/" WORD_AP "/" WORD_ADHOC ") [" WORD_INFRA "]" + +#define PROMPT_ADSL_ENCAP N_("ADSL encapsulation") +#define PROMPT_ADSL_ENCAP_CHOICES "(" NM_SETTING_ADSL_ENCAPSULATION_VCMUX "/" NM_SETTING_ADSL_ENCAPSULATION_LLC ") [none]" + +#define PROMPT_TUN_MODE N_("Tun mode") +#define WORD_TUN "tun" +#define WORD_TAP "tap" +#define PROMPT_TUN_MODE_CHOICES "(" WORD_TUN "/" WORD_TAP ") [" WORD_TUN "]" + +#define PROMPT_IP_TUNNEL_MODE N_("IP Tunnel mode") + +#define PROMPT_MACVLAN_MODE N_("MACVLAN mode") + +#define PROMPT_CONNECTION _("Connection (name, UUID, or path)") +#define PROMPT_VPN_CONNECTION _("VPN connection (name, UUID, or path)") +#define PROMPT_CONNECTIONS _("Connection(s) (name, UUID, or path)") +#define PROMPT_ACTIVE_CONNECTIONS _("Connection(s) (name, UUID, path or apath)") + +#define BASE_PROMPT "nmcli> " /* Available fields for 'connection show' */ -static NmcOutputField nmc_fields_con_show[] = { +NmcOutputField nmc_fields_con_show[] = { {"NAME", N_("NAME")}, /* 0 */ {"UUID", N_("UUID")}, /* 1 */ {"TYPE", N_("TYPE")}, /* 2 */ @@ -77,39 +138,8 @@ static NmcOutputField nmc_fields_con_show[] = { /* Helper macro to define fields */ #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[]; -extern NmcOutputField nmc_fields_setting_tun[]; -extern NmcOutputField nmc_fields_setting_ip_tunnel[]; -extern NmcOutputField nmc_fields_setting_macvlan[]; -extern NmcOutputField nmc_fields_setting_vxlan[]; - /* Available settings for 'connection show <con>' - profile part */ -static NmcOutputField nmc_fields_settings_names[] = { +NmcOutputField nmc_fields_settings_names[] = { 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 */ @@ -173,7 +203,7 @@ static NmcOutputField nmc_fields_settings_names[] = { /* Active connection data */ /* Available fields for GENERAL group */ -static NmcOutputField nmc_fields_con_active_details_general[] = { +NmcOutputField nmc_fields_con_active_details_general[] = { {"GROUP", N_("GROUP")}, /* 0 */ {"NAME", N_("NAME")}, /* 1 */ {"UUID", N_("UUID")}, /* 2 */ @@ -195,7 +225,7 @@ static NmcOutputField nmc_fields_con_active_details_general[] = { /* IP group is handled by common.c */ /* Available fields for VPN group */ -static NmcOutputField nmc_fields_con_active_details_vpn[] = { +NmcOutputField nmc_fields_con_active_details_vpn[] = { {"GROUP", N_("GROUP")}, /* 0 */ {"TYPE", N_("TYPE")}, /* 1 */ {"USERNAME", N_("USERNAME")}, /* 2 */ @@ -214,7 +244,7 @@ extern NmcOutputField nmc_fields_dhcp4_config[]; extern NmcOutputField nmc_fields_dhcp6_config[]; /* Available fields for 'connection show <con>' - active part */ -static NmcOutputField nmc_fields_con_active_details_groups[] = { +NmcOutputField nmc_fields_con_active_details_groups[] = { {"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 */ @@ -252,7 +282,7 @@ static void usage (void) { g_printerr (_("Usage: nmcli connection { COMMAND | help }\n\n" - "COMMAND := { show | up | down | add | modify | edit | delete | monitor | reload | load }\n\n" + "COMMAND := { show | up | down | add | modify | clone | edit | delete | monitor | reload | load | import | export }\n\n" " show [--active] [--order <order spec>]\n" " show [--active] [id | uuid | path | apath] <ID> ...\n\n" " up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>] [passwd-file <file with passwords>]\n\n" @@ -553,42 +583,6 @@ usage_connection_export (void) "The data are directed to standard output or to a file if a name is given.\n\n")); } -static gboolean -usage_connection_second_level (const char *cmd) -{ - gboolean ret = TRUE; - - if (matches (cmd, "show") == 0) - usage_connection_show (); - else if (matches (cmd, "up") == 0) - usage_connection_up (); - else if (matches (cmd, "down") == 0) - usage_connection_down (); - else if (matches (cmd, "add") == 0) - usage_connection_add (); - else if (matches (cmd, "modify") == 0) - usage_connection_modify (); - else if (matches (cmd, "clone") == 0) - usage_connection_clone (); - else if (matches (cmd, "edit") == 0) - usage_connection_edit (); - else if (matches (cmd, "delete") == 0) - usage_connection_delete (); - else if (matches (cmd, "monitor") == 0) - usage_connection_monitor (); - else if (matches (cmd, "reload") == 0) - usage_connection_reload (); - else if (matches (cmd, "load") == 0) - usage_connection_load (); - else if (matches (cmd, "import") == 0) - usage_connection_import (); - else if (matches (cmd, "export") == 0) - usage_connection_export (); - else - ret = FALSE; - return ret; -} - /* quit main loop */ static void quit (void) @@ -804,7 +798,8 @@ find_active_connection (const GPtrArray *active_cons, const GPtrArray *cons, const char *filter_type, const char *filter_val, - int *idx) + int *idx, + gboolean complete) { int i; int start = (idx && *idx > 0) ? *idx : 0; @@ -831,22 +826,43 @@ find_active_connection (const GPtrArray *active_cons, * 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) - && (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; + if (!filter_type || strcmp (filter_type, "id") == 0) { + if (complete) + nmc_complete_strings (filter_val, id, NULL); + if (strcmp (filter_val, id) == 0) + goto found; + } + + if (!filter_type || strcmp (filter_type, "uuid") == 0) { + if (complete && (filter_type || *filter_val)) + nmc_complete_strings (filter_val, uuid, NULL); + if (strcmp (filter_val, uuid) == 0) + goto found; + } + + if (!filter_type || strcmp (filter_type, "path") == 0) { + if (complete && (filter_type || *filter_val)) + nmc_complete_strings (filter_val, path, filter_type ? path_num : NULL, NULL); + if (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)) + goto found; } + + if (!filter_type || strcmp (filter_type, "apath") == 0) { + if (complete && (filter_type || *filter_val)) + nmc_complete_strings (filter_val, a_path, filter_type ? a_path_num : NULL, NULL); + if (g_strcmp0 (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0)) + goto found; + } + + continue; +found: + if (!idx) + return candidate; + if (found) { + *idx = i; + return found; + } + found = candidate; } if (idx) @@ -854,6 +870,19 @@ find_active_connection (const GPtrArray *active_cons, return found; } +void +nmc_active_connection_state_to_color (NMActiveConnectionState state, NmcTermColor *color) +{ + *color = NMC_TERM_COLOR_NORMAL; + + if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) + *color = NMC_TERM_COLOR_YELLOW; + else if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) + *color = NMC_TERM_COLOR_GREEN; + else if (state > NM_ACTIVE_CONNECTION_STATE_ACTIVATED) + *color = NMC_TERM_COLOR_RED; +} + static void fill_output_connection (NMConnection *connection, NmCli *nmc, gboolean active_only) { @@ -869,6 +898,7 @@ fill_output_connection (NMConnection *connection, NmCli *nmc, gboolean active_on const char *ac_state = NULL; NMActiveConnectionState ac_state_int = NM_ACTIVE_CONNECTION_STATE_UNKNOWN; char *ac_dev = NULL; + NmcTermColor color; s_con = nm_connection_get_setting_connection (connection); g_assert (s_con); @@ -897,15 +927,10 @@ fill_output_connection (NMConnection *connection, NmCli *nmc, gboolean active_on arr = nmc_dup_fields_array (nmc_fields_con_show, sizeof (nmc_fields_con_show), 0); + /* Show active connections in color */ - if (ac) { - if (ac_state_int == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) - set_val_color_all (arr, NMC_TERM_COLOR_YELLOW); - else if (ac_state_int == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) - set_val_color_all (arr, NMC_TERM_COLOR_GREEN); - else if (ac_state_int > NM_ACTIVE_CONNECTION_STATE_ACTIVATED) - set_val_color_all (arr, NMC_TERM_COLOR_RED); - } + nmc_active_connection_state_to_color (ac_state_int, &color); + set_val_color_all (arr, color); set_val_strc (arr, 0, nm_setting_connection_get_id (s_con)); set_val_strc (arr, 1, nm_setting_connection_get_uuid (s_con)); @@ -1457,7 +1482,7 @@ compare_connections (gconstpointer a, gconstpointer b, gpointer user_data) } for (i = 0; i < order->len; i++) { - item = g_array_index (order, NmcSortOrder, i); + item = g_array_index (order, NmcSortOrder, i); switch (item) { case NMC_SORT_ACTIVE: case NMC_SORT_ACTIVE_INV: @@ -1580,13 +1605,150 @@ get_invisible_active_connections (NmCli *nmc) return invisibles; } +static GArray * +parse_preferred_connection_order (const char *order, GError **error) +{ + char **strv, **iter; + const char *str; + GArray *order_arr; + NmcSortOrder val; + gboolean inverse, unique; + int i; + + strv = nmc_strsplit_set (order, ":", -1); + if (!strv || !*strv) { + g_set_error (error, NMCLI_ERROR, 0, + _("incorrect string '%s' of '--order' option"), order); + g_strfreev (strv); + return NULL; + } + + order_arr = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), 4); + for (iter = strv; iter && *iter; iter++) { + str = *iter; + inverse = FALSE; + if (str[0] == '-') + inverse = TRUE; + if (str[0] == '+' || str[0] == '-') + str++; + + if (matches (str, "active") == 0) + val = inverse ? NMC_SORT_ACTIVE_INV : NMC_SORT_ACTIVE; + else if (matches (str, "name") == 0) + val = inverse ? NMC_SORT_NAME_INV : NMC_SORT_NAME; + else if (matches (str, "type") == 0) + val = inverse ? NMC_SORT_TYPE_INV : NMC_SORT_TYPE; + else if (matches (str, "path") == 0) + val = inverse ? NMC_SORT_PATH_INV : NMC_SORT_PATH; + else { + g_array_unref (order_arr); + order_arr = NULL; + g_set_error (error, NMCLI_ERROR, 0, + _("incorrect item '%s' in '--order' option"), *iter); + break; + } + /* Check for duplicates and ignore them. */ + unique = TRUE; + for (i = 0; i < order_arr->len; i++) { + if (abs (g_array_index (order_arr, NmcSortOrder, i)) - abs (val) == 0) { + unique = FALSE; + break; + } + } + + /* Value is ok and unique, add it to the array */ + if (unique) + g_array_append_val (order_arr, val); + } + + g_strfreev (strv); + return order_arr; +} + +static NMConnection * +get_connection (NmCli *nmc, int *argc, char ***argv, int *pos, GError **error) +{ + NMConnection *connection = NULL; + const char *selector = NULL; + + if (*argc == 0) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("No connection specified")); + return NULL; + } + + if (*argc == 1 && nmc->complete) + nmc_complete_strings (**argv, "id", "uuid", "path", NULL); + + if ( strcmp (**argv, "id") == 0 + || strcmp (**argv, "uuid") == 0 + || strcmp (**argv, "path") == 0) { + selector = **argv; + if (next_arg (argc, argv) != 0) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("%s argument is missing"), selector); + return NULL; + } + } + + connection = nmc_find_connection (nmc->connections, selector, **argv, pos, + *argc == 1 && nmc->complete); + if (!connection) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, + _("unknown connection '%s'"), **argv); + } + + /* If the caller wants multiple results (pos is set) and there are any, + * don't switch to next argument. + */ + if (!pos || !*pos) + next_arg (argc, argv); + + return connection; +} + static NMCResultCode -do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets, - const GArray *order, int argc, char **argv) +do_connections_show (NmCli *nmc, int argc, char **argv) { GError *err = NULL; char *profile_flds = NULL, *active_flds = NULL; GPtrArray *invisibles, *sorted_cons; + gboolean active_only = FALSE; + gboolean show_secrets = FALSE; + GArray *order = NULL; + int i; + + /* check connection show options [--active] [--show-secrets] */ + for (i = 0; i < 3; i++) { + if (argc == 1 && nmc->complete) { + nmc_complete_strings (*argv, "--active", "--show-secrets", + "--order", NULL); + } + + if (!active_only && nmc_arg_is_option (*argv, "active")) { + active_only = TRUE; + next_arg (&argc, &argv); + } else if (!show_secrets && nmc_arg_is_option (*argv, "show-secrets")) { + /* --show-secrets is deprecated in favour of global --show-secrets */ + /* Keep it here for backwards compatibility */ + show_secrets = TRUE; + next_arg (&argc, &argv); + } else if (!order && nmc_arg_is_option (*argv, "order")) { + if (next_arg (&argc, &argv) != 0) { + g_set_error_literal (&err, NMCLI_ERROR, 0, + _("'--order' argument is missing")); + goto finish; + } + /* TODO: complete --order */ + order = parse_preferred_connection_order (*argv, &err); + if (err) + goto finish; + next_arg (&argc, &argv); + } else { + break; + } + } + show_secrets = nmc->show_secrets || show_secrets; if (argc == 0) { char *fields_str; @@ -1594,7 +1756,9 @@ do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets, char *fields_common = NMC_FIELDS_CON_SHOW_COMMON; NmcOutputField *tmpl, *arr; size_t tmpl_len; - int i; + + if (nmc->complete) + goto finish; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; @@ -1654,6 +1818,9 @@ do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets, NMActiveConnection *acon = NULL; const char *selector = NULL; + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "id", "uuid", "path", "apath", NULL); + if ( strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0 || strcmp (*argv, "path") == 0 @@ -1666,10 +1833,13 @@ do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets, } } - /* Find connection by id, uuid, path or apath */ - con = nmc_find_connection (nmc->connections, selector, *argv, &pos); - if (!con) { - acon = find_active_connection (active_cons, nmc->connections, selector, *argv, NULL); + /* Try to find connection by id, uuid or path first */ + con = nmc_find_connection (nmc->connections, selector, *argv, &pos, + argc == 1 && nmc->complete); + if (!con && (!selector || strcmp (selector, "apath") == 0)) { + /* Try apath too */ + acon = find_active_connection (active_cons, nmc->connections, "apath", *argv, NULL, + argc == 1 && nmc->complete); if (acon) con = NM_CONNECTION (nm_active_connection_get_connection (acon)); } @@ -1694,6 +1864,11 @@ do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets, continue; } + if (nmc->complete) { + next_arg (&argc, &argv); + continue; + } + /* Show an empty line between connections */ if (new_line) g_print ("\n"); @@ -1740,6 +1915,8 @@ finish: } g_free (profile_flds); g_free (active_flds); + if (order) + g_array_unref (order); return nmc->return_value; } @@ -1854,17 +2031,22 @@ find_device_for_connection (NmCli *nmc, if (iface) { const char *dev_iface = nm_device_get_iface (dev); - if ( !g_strcmp0 (dev_iface, iface) - && nm_device_connection_compatible (dev, connection, NULL)) { - found_device = dev; + if (!nm_streq0 (dev_iface, iface)) + continue; + + if (!nm_device_connection_compatible (dev, connection, error)) { + g_prefix_error (error, _("device '%s' not compatible with connection '%s':"), + iface, nm_setting_connection_get_id (s_con)); + return FALSE; } + } else { - if (nm_device_connection_compatible (dev, connection, NULL)) { - found_device = dev; - } + if (!nm_device_connection_compatible (dev, connection, NULL)) + continue; } - if (found_device && ap && !strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME) && NM_IS_DEVICE_WIFI (dev)) { + found_device = dev; + if (ap && !strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME) && NM_IS_DEVICE_WIFI (dev)) { char *bssid_up = g_ascii_strup (ap, -1); const GPtrArray *aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev)); found_device = NULL; /* Mark as not found; set to the device again later, only if AP matches */ @@ -1899,6 +2081,14 @@ find_device_for_connection (NmCli *nmc, } } +typedef struct { + NmCli *nmc; + NMDevice *device; + NMActiveConnection *active; +} ActivateConnectionInfo; + +static void activate_connection_info_finish (ActivateConnectionInfo *info); + static const char * vpn_connection_state_reason_to_string (NMVpnConnectionStateReason reason) { @@ -1933,9 +2123,9 @@ vpn_connection_state_reason_to_string (NMVpnConnectionStateReason reason) } static void -device_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) +device_state_cb (NMDevice *device, GParamSpec *pspec, ActivateConnectionInfo *info) { - NmCli *nmc = (NmCli *) user_data; + NmCli *nmc = info->nmc; NMActiveConnection *active; NMDeviceState state; NMActiveConnectionState ac_state; @@ -1950,7 +2140,7 @@ device_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) nmc_terminal_erase_line (); g_print (_("Connection successfully activated (D-Bus active path: %s)\n"), nm_object_get_path (NM_OBJECT (active))); - quit (); + activate_connection_info_finish (info); } else if ( ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING && state >= NM_DEVICE_STATE_IP_CONFIG && state <= NM_DEVICE_STATE_ACTIVATED) { @@ -1958,24 +2148,26 @@ device_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) nmc_terminal_erase_line (); g_print (_("Connection successfully activated (master waiting for slaves) (D-Bus active path: %s)\n"), nm_object_get_path (NM_OBJECT (active))); - quit (); - } else if ( ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING - && state == NM_DEVICE_STATE_FAILED) { - if (nmc->print_output == NMC_PRINT_PRETTY) - nmc_terminal_erase_line (); - g_print (_("Error: Connection activation failed.\n")); - quit (); - } else if (active && ac_state != NM_ACTIVE_CONNECTION_STATE_ACTIVATING) { + activate_connection_info_finish (info); + } +} + +static void +active_connection_removed_cb (NMClient *client, NMActiveConnection *active, ActivateConnectionInfo *info) +{ + NmCli *nmc = info->nmc; + + if (active == info->active) { g_string_printf (nmc->return_text, _("Error: Connection activation failed.")); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; - quit (); + activate_connection_info_finish (info); } } static void -active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpointer user_data) +active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, ActivateConnectionInfo *info) { - NmCli *nmc = (NmCli *) user_data; + NmCli *nmc = info->nmc; NMActiveConnectionState state; state = nm_active_connection_get_state (active); @@ -1985,13 +2177,7 @@ active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpoin nmc_terminal_erase_line (); g_print (_("Connection successfully activated (D-Bus active path: %s)\n"), nm_object_get_path (NM_OBJECT (active))); - g_object_unref (active); - quit (); - } else if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) { - g_string_printf (nmc->return_text, _("Error: Connection activation failed.")); - nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; - g_object_unref (active); - quit (); + activate_connection_info_finish (info); } else if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) { /* activating master connection does not automatically activate any slaves, so their * active connection state will not progress beyond ACTIVATING state. @@ -2012,10 +2198,8 @@ active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpoin && ( NM_IS_DEVICE_BOND (device) || NM_IS_DEVICE_TEAM (device) || NM_IS_DEVICE_BRIDGE (device))) { - g_signal_handlers_disconnect_by_func (active, G_CALLBACK (active_connection_state_cb), nmc); - g_signal_connect (device, "notify::" NM_DEVICE_STATE, G_CALLBACK (device_state_cb), nmc); - - device_state_cb (device, NULL, nmc); + g_signal_connect (device, "notify::" NM_DEVICE_STATE, G_CALLBACK (device_state_cb), info); + device_state_cb (device, NULL, info); } } } @@ -2024,9 +2208,9 @@ static void vpn_connection_state_cb (NMVpnConnection *vpn, NMVpnConnectionState state, NMVpnConnectionStateReason reason, - gpointer user_data) + ActivateConnectionInfo *info) { - NmCli *nmc = (NmCli *) user_data; + NmCli *nmc = info->nmc; switch (state) { case NM_VPN_CONNECTION_STATE_PREPARE: @@ -2041,8 +2225,7 @@ vpn_connection_state_cb (NMVpnConnection *vpn, nmc_terminal_erase_line (); g_print (_("VPN connection successfully activated (D-Bus active path: %s)\n"), nm_object_get_path (NM_OBJECT (vpn))); - g_object_unref (vpn); - quit (); + activate_connection_info_finish (info); break; case NM_VPN_CONNECTION_STATE_FAILED: @@ -2050,8 +2233,7 @@ vpn_connection_state_cb (NMVpnConnection *vpn, g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."), vpn_connection_state_reason_to_string (reason)); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; - g_object_unref (vpn); - quit (); + activate_connection_info_finish (info); break; default: @@ -2059,16 +2241,21 @@ vpn_connection_state_cb (NMVpnConnection *vpn, } } -static gboolean -timeout_cb (gpointer user_data) +static void +set_nmc_error_timeout (NmCli *nmc) { - /* Time expired -> exit nmcli */ + g_string_printf (nmc->return_text, _("Error: Timeout expired (%d seconds)"), nmc->timeout); + nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED; +} - NmCli *nmc = (NmCli *) user_data; +static gboolean +activate_connection_timeout_cb (gpointer user_data) +{ + ActivateConnectionInfo *info = user_data; - g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout); - nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED; - quit (); + /* Time expired -> exit nmcli */ + set_nmc_error_timeout (info->nmc); + activate_connection_info_finish (info); return FALSE; } @@ -2107,10 +2294,27 @@ progress_vpn_cb (gpointer user_data) return TRUE; } -typedef struct { - NmCli *nmc; - NMDevice *device; -} ActivateConnectionInfo; +static void +activate_connection_info_finish (ActivateConnectionInfo *info) +{ + if (info->device) { + g_signal_handlers_disconnect_by_func (info->device, G_CALLBACK (device_state_cb), info); + g_object_unref (info->device); + } + + if (info->active) { + if (NM_IS_VPN_CONNECTION (info->active)) + g_signal_handlers_disconnect_by_func (info->active, G_CALLBACK (vpn_connection_state_cb), info); + else + g_signal_handlers_disconnect_by_func (info->active, G_CALLBACK (active_connection_state_cb), info); + g_object_unref (info->active); + + } + g_signal_handlers_disconnect_by_func (info->nmc->client, G_CALLBACK (active_connection_removed_cb), info); + + g_free (info); + quit (); +} static void activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_data) @@ -2123,20 +2327,22 @@ activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_dat const GPtrArray *ac_devs; GError *error = NULL; - active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error); + info->active = active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error); if (error) { g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"), error->message); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; - quit (); + activate_connection_info_finish (info); } else { state = nm_active_connection_get_state (active); if (!device) { /* device could be NULL for virtual devices. Fill it here. */ ac_devs = nm_active_connection_get_devices (active); - info->device = device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL; + device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL; + if (device) + info->device = g_object_ref (device); } if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { @@ -2147,12 +2353,11 @@ activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_dat g_print (_("Connection successfully activated (D-Bus active path: %s)\n"), nm_object_get_path (NM_OBJECT (active))); } - g_object_unref (active); - quit (); + activate_connection_info_finish (info); } else { if (NM_IS_VPN_CONNECTION (active)) { /* Monitor VPN state */ - g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc); + g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), info); /* Start progress indication showing VPN states */ if (nmc->print_output == NMC_PRINT_PRETTY) { @@ -2161,8 +2366,8 @@ activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_dat progress_id = g_timeout_add (120, progress_vpn_cb, NM_VPN_CONNECTION (active)); } } else { - g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), nmc); - active_connection_state_cb (active, NULL, nmc); + g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), info); + active_connection_state_cb (active, NULL, info); /* Start progress indication showing device states */ if (nmc->print_output == NMC_PRINT_PRETTY) { @@ -2173,10 +2378,13 @@ activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_dat } /* Start timer not to loop forever when signals are not emitted */ - g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); + g_timeout_add_seconds (nmc->timeout, activate_connection_timeout_cb, info); + + /* Fail when the active connection goes away. */ + g_signal_connect (nmc->client, NM_CLIENT_ACTIVE_CONNECTION_REMOVED, + G_CALLBACK (active_connection_removed_cb), info); } } - g_free (info); } /** @@ -2331,7 +2539,8 @@ nmc_activate_connection (NmCli *nmc, info = g_malloc0 (sizeof (ActivateConnectionInfo)); info->nmc = nmc; - info->device = device; + if (device) + info->device = g_object_ref (device); nm_client_activate_connection_async (nmc->client, connection, @@ -2351,10 +2560,11 @@ do_connection_up (NmCli *nmc, int argc, char **argv) const char *ap = NULL; const char *nsp = NULL; const char *pwds = NULL; - GError *error = NULL; - const char *selector = NULL; - const char *name = NULL; - char *line = NULL; + gs_free_error GError *error = NULL; + char **arg_arr = NULL; + int arg_num; + char ***argv_ptr = &argv; + int *argc_ptr = &argc; /* * Set default timeout for connection activation. @@ -2363,66 +2573,63 @@ do_connection_up (NmCli *nmc, int argc, char **argv) if (nmc->timeout == -1) nmc->timeout = 90; - if (argc == 0) { - if (nmc->ask) { - line = nmc_readline (PROMPT_CONNECTION); - name = line ? line : ""; - } - } else if (strcmp (*argv, "ifname") != 0) { - if ( strcmp (*argv, "id") == 0 - || strcmp (*argv, "uuid") == 0 - || strcmp (*argv, "path") == 0) { + if (argc == 0 && nmc->ask) { + char *line; - selector = *argv; - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - name = *argv; - } - name = *argv; - next_arg (&argc, &argv); + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + + line = nmc_readline ("%s: ", PROMPT_CONNECTION); + nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); + g_free (line); + argv_ptr = &arg_arr; + argc_ptr = &arg_num; } - if (name) { - connection = nmc_find_connection (nmc->connections, selector, name, NULL); + if (argc > 0 && strcmp (*argv, "ifname") != 0) { + connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, &error); if (!connection) { - g_string_printf (nmc->return_text, _("Error: Connection '%s' does not exist."), name); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return error->code; } } while (argc > 0) { + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "ifname", "ap", "passwd-file", NULL); + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } ifname = *argv; + if (argc == 1 && nmc->complete) + nmc_complete_device (nmc->client, ifname, ap != NULL); } else if (strcmp (*argv, "ap") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } ap = *argv; + if (argc == 1 && nmc->complete) + nmc_complete_bssid (nmc->client, ifname, ap); } else if (strcmp (*argv, "passwd-file") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } + if (argc == 1 && nmc->complete) + nmc->return_value = NMC_RESULT_COMPLETE_FILE; + pwds = *argv; } - else { + else if (!nmc->complete) { g_printerr (_("Unknown parameter: %s\n"), *argv); } @@ -2430,6 +2637,9 @@ do_connection_up (NmCli *nmc, int argc, char **argv) argv++; } + if (nmc->complete) + return nmc->return_value; + /* Use nowait_flag instead of should_wait because exiting has to be postponed till * active_connection_state_cb() is called. That gives NM time to check our permissions * and we can follow activation progress. @@ -2440,18 +2650,14 @@ do_connection_up (NmCli *nmc, int argc, char **argv) if (!nmc_activate_connection (nmc, connection, ifname, ap, nsp, pwds, activate_connection_cb, &error)) { g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = error->code; - g_clear_error (&error); nmc->should_wait--; - goto error; + return error->code; } /* Start progress indication */ if (nmc->print_output == NMC_PRINT_PRETTY) progress_id = g_timeout_add (120, progress_cb, _("preparing")); -error: - g_free (line); return nmc->return_value; } @@ -2499,7 +2705,7 @@ connection_op_timeout_cb (gpointer user_data) { ConnectionCbInfo *info = user_data; - timeout_cb (info->nmc); + set_nmc_error_timeout (info->nmc); connection_cb_info_finish (info, NULL); return G_SOURCE_REMOVE; } @@ -2549,6 +2755,9 @@ do_connection_down (NmCli *nmc, int argc, char **argv) nmc->timeout = 10; if (argc == 0) { + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + if (nmc->ask) { char *line = nmc_readline (PROMPT_ACTIVE_CONNECTIONS); nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); @@ -2558,7 +2767,7 @@ do_connection_down (NmCli *nmc, int argc, char **argv) if (arg_num == 0) { g_string_printf (nmc->return_text, _("Error: No connection specified.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } } @@ -2567,6 +2776,9 @@ do_connection_down (NmCli *nmc, int argc, char **argv) while (arg_num > 0) { const char *selector = NULL; + if (arg_num == 1) + nmc_complete_strings (*arg_ptr, "id", "uuid", "path", "apath", NULL); + if ( strcmp (*arg_ptr, "id") == 0 || strcmp (*arg_ptr, "uuid") == 0 || strcmp (*arg_ptr, "path") == 0 @@ -2576,11 +2788,12 @@ do_connection_down (NmCli *nmc, int argc, char **argv) if (next_arg (&arg_num, &arg_ptr) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } } - active = find_active_connection (active_cons, nmc->connections, selector, *arg_ptr, &idx); + active = find_active_connection (active_cons, nmc->connections, selector, *arg_ptr, &idx, + arg_num == 1 && nmc->complete); if (active) { /* Check if the connection is unique. */ /* Calling down for the same connection repeatedly would result in @@ -2588,7 +2801,8 @@ do_connection_down (NmCli *nmc, int argc, char **argv) if (!g_slist_find (queue, active)) queue = g_slist_prepend (queue, g_object_ref (active)); } else { - g_printerr (_("Error: '%s' is not an active connection.\n"), *arg_ptr); + if (!nmc->complete) + g_printerr (_("Error: '%s' is not an active connection.\n"), *arg_ptr); g_string_printf (nmc->return_text, _("Error: not all active connections found.")); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; } @@ -2600,7 +2814,10 @@ do_connection_down (NmCli *nmc, int argc, char **argv) if (!queue) { g_string_printf (nmc->return_text, _("Error: no active connection provided.")); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + goto finish; + } else if (nmc->complete) { + g_slist_free (queue); + goto finish; } queue = g_slist_reverse (queue); @@ -2626,7 +2843,7 @@ do_connection_down (NmCli *nmc, int argc, char **argv) nm_client_deactivate_connection (nmc->client, active, NULL, NULL); } -error: +finish: g_strfreev (arg_arr); return nmc->return_value; } @@ -2802,7 +3019,7 @@ static const NameItem nmc_vxlan_settings [] = { /* Available connection types */ static const NameItem nmc_valid_connection_types[] = { - { NM_SETTING_GENERIC_SETTING_NAME, NULL, nmc_generic_settings }, + { NM_SETTING_GENERIC_SETTING_NAME, NULL, nmc_generic_settings }, /* Needs to be first. */ { NM_SETTING_WIRED_SETTING_NAME, "ethernet", nmc_ethernet_settings }, { NM_SETTING_PPPOE_SETTING_NAME, NULL, nmc_pppoe_settings }, { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", nmc_wifi_settings }, @@ -2830,26 +3047,37 @@ static const NameItem nmc_valid_connection_types[] = { }; /* - * Return an alias for the 'name' if exists, else return the 'name'. - * The returned string must not be freed. + * Return the most approopriate name for the connection of a type 'name' possibly with given 'slave_type' + * if exists, else return the 'name'. The returned string must not be freed. */ static const char * -get_name_alias (const char *name, const NameItem array[]) +get_name_alias (const char *name, const char *slave_type, const NameItem array[]) { const NameItem *iter = &array[0]; - if (!name) + if (slave_type) { + while (iter && iter->name) { + if ( g_str_has_prefix (iter->name, slave_type) + && g_str_has_suffix (iter->name, "-slave")) + break; + iter++; + } + } else if (name) { + while (iter && iter->name) { + if (!strcmp (name, iter->name)) + break; + iter++; + } + } else return NULL; - while (iter && iter->name) { - if (!strcmp (name, iter->name)) { - if (iter->alias) - return iter->alias; - else - return iter->name; - } - iter++; + if (iter) { + if (iter->alias) + return iter->alias; + else + return iter->name; } + return name; } @@ -2887,17 +3115,121 @@ get_valid_settings_array (const char *con_type) { guint i, num; + /* No connection type yet? Return settings for a generic connection + * (just the "connection" setting), which always makes sense. */ if (!con_type) - return NULL; + return nmc_valid_connection_types[0].settings; num = G_N_ELEMENTS (nmc_valid_connection_types); for (i = 0; i < num; i++) { if (nm_streq0 (con_type, nmc_valid_connection_types[i].name)) return nmc_valid_connection_types[i].settings; } + return NULL; } +/* get_valid_properties_string: + * @array: base properties for the current connection type + * @array_slv: slave properties (or ipv4/ipv6 ones) for the current connection type + * @modifier: to prepend to each element of the returned list + * @prefix: only properties matching the prefix will be returned + * @postfix: required prefix on the property args; if a empty string is passed, is + * assumed that the @prefix is a shortcut, so it should not be completed + * but left as is (and an additional check for shortcut ambiguity is performed) + * + * Returns a list of properties compatible with the current connection type + * for the shell autocompletion functionality. + * + * Returns: list of property.arg elements + */ +static char * +get_valid_properties_string (const NameItem *array, + const NameItem *array_slv, + char modifier, + const char *prefix, + const char *postfix) +{ + const NameItem *iter = array; + const NmcOutputField *field_iter; + const char *prop_name = NULL; + GString *str; + int i, j; + gboolean full_match = FALSE; + + g_return_val_if_fail (prefix, NULL); + + str = g_string_sized_new (1024); + + for (i = 0; i < 2; i++, iter = array_slv) { + while (!full_match && iter && iter->name) { + if ( !(g_str_has_prefix (iter->name, prefix)) + && (!(iter->alias) || !g_str_has_prefix (iter->alias, prefix))) { + iter++; + continue; + } + /* If postix (so prefix is terminated by a dot), check + * that prefix is not ambiguous */ + if (postfix) { + /* If we have a perfect match, no need to look for others + * prefix and no check on ambiguity should be performed. + * Moreover, erase previous matches from output string */ + if ( nm_streq (prefix, iter->name) + || nm_streq0 (prefix, iter->alias)) { + g_string_erase (str, 0, -1); + full_match = TRUE; + } else if (prop_name) { + return g_string_free (str, TRUE); + } + prop_name = prefix; + } else { + prop_name = iter->name; + } + + /* Search the array with the arguments of the current property */ + j = 0; + while (!nm_streq0 (iter->name, nmc_fields_settings_names[j].name)) { + g_assert (nmc_fields_settings_names[j].name); + j++; + } + field_iter = nmc_fields_settings_names[j].group; + + j = 0; + while (field_iter[j].name) { + gchar *new; + const char *arg_name = field_iter[j].name; + + /* If required, expand the alias too */ + if (!postfix && iter->alias) { + if (modifier) + g_string_append_c (str, modifier); + new = g_strdup_printf ("%s.%s\n", + iter->alias, + arg_name); + g_string_append (str, new); + g_free (new); + } + + if (postfix && !g_str_has_prefix (arg_name, postfix)) { + j++; + continue; + } + + if (modifier) + g_string_append_c (str, modifier); + new = g_strdup_printf ("%s.%s\n", + prop_name, + arg_name); + g_string_append (str, new); + g_free (new); + j++; + } + iter++; + } + } + return g_string_free (str, FALSE); +} + /* * Check if 'val' is valid string in either array->name or array->alias for * both array parameters (array & array_slv). @@ -2940,8 +3272,7 @@ check_valid_name (const char *val, const NameItem *array, const NameItem *array_ /* We want to handle aliases, so construct own error message */ char *err_str = get_valid_options_string (array, array_slv); - g_set_error (error, 1, 0, _("'%s' not among [%s]"), - val, err_str); + g_set_error (error, 1, 0, _("'%s' not among [%s]"), val, err_str); g_free (err_str); g_clear_error (&tmp_err); } @@ -3004,306 +3335,6 @@ is_setting_mandatory (NMConnection *connection, NMSetting *setting) /*----------------------------------------------------------------------------*/ -static gboolean -check_mac (const char *mac, - int type, - const char *keyword, - GError **error) -{ - g_return_val_if_fail (type == ARPHRD_ETHER || type == ARPHRD_INFINIBAND, FALSE); - - if (!mac) - return TRUE; - - if (!nm_utils_hwaddr_valid (mac, nm_utils_hwaddr_len (type))) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: '%s': '%s' is not a valid %s MAC address."), - keyword, mac, type == ARPHRD_INFINIBAND ? _("InfiniBand") : _("Ethernet")); - return FALSE; - } - - return TRUE; -} - -static gboolean -check_and_convert_mtu (const char *mtu, guint32 *mtu_int, GError **error) -{ - unsigned long local_mtu_int; - - if (mtu_int) - *mtu_int = 0; - - if (!mtu) - return TRUE; - - if (!nmc_string_to_uint (mtu, TRUE, 0, G_MAXUINT32, &local_mtu_int)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'mtu': '%s' is not a valid MTU."), mtu); - return FALSE; - } - if (mtu_int) - *mtu_int = (guint32) local_mtu_int; - return TRUE; -} - -static gboolean -check_infiniband_parent (const char *parent, GError **error) -{ - if (!parent) - return TRUE; - - if (!nm_utils_iface_valid_name (parent)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'parent': '%s' is not a valid interface name."), parent); - return FALSE; - } - return TRUE; -} - - -static gboolean -check_infiniband_p_key (const char *p_key, guint32 *p_key_int, GError **error) -{ - unsigned long local_p_key_int; - gboolean p_key_valid = FALSE; - if (!p_key) - return TRUE; - - if (!strncmp (p_key, "0x", 2)) - p_key_valid = nmc_string_to_uint_base (p_key + 2, 16, TRUE, 0, G_MAXUINT16, &local_p_key_int); - else - p_key_valid = nmc_string_to_uint (p_key, TRUE, 0, G_MAXUINT16, &local_p_key_int); - if (!p_key_valid) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'p-key': '%s' is not a valid InfiniBand P_KEY."), p_key); - return FALSE; - } - if (p_key_int) - *p_key_int = (guint32) local_p_key_int; - return TRUE; -} - -static gboolean -check_user_group_id (const char *id, GError **error) -{ - unsigned long int value; - - if (!nmc_string_to_uint (id, FALSE, 0, 0, &value)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: '%s' is not a valid UID/GID."), id); - return FALSE; - } - - return TRUE; -} - -/** - * check_valid_enumeration: - * @str: string to check against string array @strings - * @strings: string array to check @str againt - * @what: what parameter @str belongs to (used in error message) - * @what_desc: longer description of @what parameter (used in error message) - * @error: location to store an error, or %NULL - * - * Check whether @str is one of the string of @strings array. It accepts - * shortcuts and normalizes them (@str argument is modified on success). - * - * Returns: %TRUE on success, %FALSE on failure - */ -static gboolean -check_valid_enumeration (char **str, - const char *strings[], - const char *what, - const char *what_desc, - GError **error) -{ - char *tmp; - const char *checked_str; - - if (!str || !*str) - return TRUE; - - tmp = g_strstrip (g_strdup (*str)); - checked_str = nmc_string_is_valid (tmp, strings, NULL); - g_free (tmp); - if (checked_str) { - g_free (*str); - *str = g_strdup (checked_str); - } else { - char *options; - - options = nmc_util_strv_for_display (strings, TRUE); - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: '%s': '%s' is not a valid %s %s."), - what, *str, what_desc, options); - g_free (options); - } - return !!checked_str; -} - -/* Checks Wi-Fi mode. */ -static gboolean -check_wifi_mode (char **mode, GError **error) -{ - const char *modes[] = { "infrastructure", "ap", "adhoc", NULL }; - - return check_valid_enumeration (mode, modes, "mode", _("Wi-Fi mode"), error); -} - -/* Checks InfiniBand mode. */ -static gboolean -check_infiniband_mode (char **mode, GError **error) -{ - const char *modes[] = { "datagram", "connected", NULL }; - - return check_valid_enumeration (mode, modes, "mode", _("InfiniBand transport mode"), error); -} - -/* Checks ADSL protocol */ -static gboolean -check_adsl_protocol (char **protocol, GError **error) -{ - const char *protos[] = { NM_SETTING_ADSL_PROTOCOL_PPPOA, - NM_SETTING_ADSL_PROTOCOL_PPPOE, - NM_SETTING_ADSL_PROTOCOL_IPOATM, - NULL }; - - return check_valid_enumeration (protocol, protos, "protocol", _("ADSL protocol"), error); -} - -/* Checks ADSL encapsulation */ -static gboolean -check_adsl_encapsulation (char **encapsulation, GError **error) -{ - const char *modes[] = { NM_SETTING_ADSL_ENCAPSULATION_VCMUX, - NM_SETTING_ADSL_ENCAPSULATION_LLC, - NULL }; - - return check_valid_enumeration (encapsulation, modes, "encapsulation", _("ADSL encapsulation"), error); -} - -/* Checks TUN mode. */ -static gboolean -check_tun_mode (char **mode, GError **error) -{ - const char *modes[] = { "tun", "tap", NULL }; - - return check_valid_enumeration (mode, modes, "mode", _("TUN device mode"), error); -} - -static gboolean -check_and_convert_vlan_flags (const char *flags, guint32 *flags_int, GError **error) -{ - unsigned long local_flags_int; - - if (!flags) - return TRUE; - - if (!nmc_string_to_uint (flags, TRUE, 0, 7, &local_flags_int)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'flags': '%s' is not valid; use <0-7>."), flags); - return FALSE; - } - if (flags_int) - *flags_int = (guint32) local_flags_int; - return TRUE; -} - -static gboolean -check_and_convert_vlan_prio_maps (const char *prio_map, - NMVlanPriorityMap type, - char ***prio_map_arr, - GError **error) -{ - char **local_prio_map_arr; - GError *local_err = NULL; - - if (!prio_map) - return TRUE; - - if (!(local_prio_map_arr = nmc_vlan_parse_priority_maps (prio_map, type, &local_err))) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: '%s': '%s' is not valid; %s "), - type == NM_VLAN_INGRESS_MAP ? "ingress" : "egress", - prio_map, local_err->message); - return FALSE; - } - - if (prio_map_arr) - *prio_map_arr = local_prio_map_arr; - return TRUE; -} - -static gboolean -add_ip4_address_to_connection (NMIPAddress *ip4addr, NMConnection *connection) -{ - NMSettingIPConfig *s_ip4; - gboolean ret; - - if (!ip4addr) - return TRUE; - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (!s_ip4) { - s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, - NULL); - } - ret = nm_setting_ip_config_add_address (s_ip4, ip4addr); - nm_ip_address_unref (ip4addr); - - return ret; -} - -static gboolean -add_ip6_address_to_connection (NMIPAddress *ip6addr, NMConnection *connection) -{ - NMSettingIPConfig *s_ip6; - gboolean ret; - - if (!ip6addr) - return TRUE; - - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (!s_ip6) { - s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); - nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, - NULL); - } - ret = nm_setting_ip_config_add_address (s_ip6, ip6addr); - nm_ip_address_unref (ip6addr); - - return ret; -} - -static char * -unique_master_iface_ifname (const GPtrArray *connections, - const char *try_name) -{ - NMConnection *connection; - char *new_name; - unsigned int num = 1; - int i = 0; - const char *ifname = NULL; - - new_name = g_strdup (try_name); - while (i < connections->len) { - connection = NM_CONNECTION (connections->pdata[i]); - ifname = nm_connection_get_interface_name (connection); - if (g_strcmp0 (new_name, ifname) == 0) { - g_free (new_name); - new_name = g_strdup_printf ("%s%d", try_name, num++); - i = 0; - } else - i++; - } - return new_name; -} - static const char * _strip_master_prefix (const char *master, const char *(**func)(NMConnection *)) { @@ -3410,29 +3441,6 @@ normalized_master_for_slave (const GPtrArray *connections, return out_master; } -static gboolean -bridge_prop_string_to_uint (const char *str, - const char *nmc_arg, - GType bridge_type, - const char *propname, - unsigned long *out_val, - GError **error) -{ - GParamSpecUInt *pspec; - - pspec = (GParamSpecUInt *) g_object_class_find_property (g_type_class_peek (bridge_type), - propname); - g_assert (G_IS_PARAM_SPEC_UINT (pspec)); - - if (!nmc_string_to_uint (str, TRUE, pspec->minimum, pspec->maximum, out_val)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: '%s': '%s' is not valid; use <%u-%u>."), - nmc_arg, str, pspec->minimum, pspec->maximum); - return FALSE; - } - return TRUE; -} - #define WORD_YES "yes" #define WORD_NO "no" #define WORD_LOC_YES _("yes") @@ -3452,3192 +3460,1069 @@ prompt_yes_no (gboolean default_yes, char *delim) return prompt; } -static gboolean -normalize_yes_no (char **yes_no) +static NMSetting * +is_setting_valid (NMConnection *connection, const NameItem *valid_settings_main, const NameItem *valid_settings_slave, char *setting) { - char *tmp; - const char *checked_yes_no; - const char *strv[] = { WORD_LOC_YES, WORD_LOC_NO, NULL }; - - if (!yes_no || !*yes_no) - return FALSE; + const char *setting_name; - tmp = g_strstrip (g_strdup (*yes_no)); - checked_yes_no = nmc_string_is_valid (tmp, strv, NULL); - g_free (tmp); - if (g_strcmp0 (checked_yes_no, WORD_LOC_YES) == 0) { - g_free (*yes_no); - *yes_no = g_strdup (WORD_YES); - } else if (g_strcmp0 (checked_yes_no, WORD_LOC_NO) == 0) { - g_free (*yes_no); - *yes_no = g_strdup (WORD_NO); - } - return !!checked_yes_no; + if (!(setting_name = check_valid_name (setting, valid_settings_main, valid_settings_slave, NULL))) + return NULL; + return nm_connection_get_setting_by_name (connection, setting_name); } -static gboolean -want_provide_opt_args (const char *type, int num) +static char * +is_property_valid (NMSetting *setting, const char *property, GError **error) { - char *answer; - gboolean ret = TRUE; + char **valid_props = NULL; + const char *prop_name; + char *ret; - /* Ask for optional arguments. */ - g_print (ngettext ("There is %d optional argument for '%s' connection type.\n", - "There are %d optional arguments for '%s' connection type.\n", num), - num, type); - answer = nmc_readline (ngettext ("Do you want to provide it? %s", - "Do you want to provide them? %s", num), - prompt_yes_no (TRUE, NULL)); - answer = answer ? g_strstrip (answer) : NULL; - if (answer && matches (answer, WORD_LOC_YES) != 0) - ret = FALSE; - g_free (answer); + valid_props = nmc_setting_get_valid_properties (setting); + prop_name = nmc_string_is_valid (property, (const char **) valid_props, error); + ret = g_strdup (prop_name); + g_strfreev (valid_props); return ret; } -static void -do_questionnaire_ethernet (gboolean ethernet, char **mtu, char **mac, char **cloned_mac) +static char * +unique_master_iface_ifname (const GPtrArray *connections, + const char *try_name) { - gboolean once_more; - GError *error = NULL; - - /* Ask for optional arguments */ - if (ethernet && !want_provide_opt_args (_("ethernet"), 3)) - return; + NMConnection *connection; + char *new_name; + unsigned int num = 1; + int i = 0; + const char *ifname = NULL; - if (!*mtu) { - do { - *mtu = nmc_readline (_("MTU [auto]: ")); - once_more = !check_and_convert_mtu (*mtu, NULL, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*mtu); - } - } while (once_more); - } - if (!*mac) { - do { - *mac = nmc_readline (_("MAC [none]: ")); - once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*mac); - } - } while (once_more); - } - if (!*cloned_mac) { - do { - *cloned_mac = nmc_readline (_("Cloned MAC [none]: ")); - once_more = !check_mac (*cloned_mac, ARPHRD_ETHER, "cloned-mac", &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*cloned_mac); - } - } while (once_more); + new_name = g_strdup (try_name); + while (i < connections->len) { + connection = NM_CONNECTION (connections->pdata[i]); + ifname = nm_connection_get_interface_name (connection); + if (g_strcmp0 (new_name, ifname) == 0) { + g_free (new_name); + new_name = g_strdup_printf ("%s%d", try_name, num++); + i = 0; + } else + i++; } + return new_name; } -#define WORD_DATAGRAM "datagram" -#define WORD_CONNECTED "connected" -#define PROMPT_IB_MODE "(" WORD_DATAGRAM "/" WORD_CONNECTED ") [" WORD_DATAGRAM "]: " static void -do_questionnaire_infiniband (char **mtu, char **mac, char **mode, char **parent, char **p_key) +set_default_interface_name (NmCli *nmc, NMSettingConnection *s_con) { - gboolean once_more; - GError *error = NULL; + char *ifname = NULL; + const char *con_type = nm_setting_connection_get_connection_type (s_con); - /* Ask for optional arguments */ - if (!want_provide_opt_args (_("InfiniBand"), 5)) + if (nm_setting_connection_get_interface_name (s_con)) return; - if (!*mtu) { - do { - *mtu = nmc_readline (_("MTU [auto]: ")); - once_more = !check_and_convert_mtu (*mtu, NULL, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*mtu); - } - } while (once_more); - } - if (!*mac) { - do { - *mac = nmc_readline (_("MAC [none]: ")); - once_more = !check_mac (*mac, ARPHRD_INFINIBAND, "mac", &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*mac); - } - } while (once_more); - } - if (!*mode) { - do { - *mode = nmc_readline (_("Transport mode %s"), PROMPT_IB_MODE); - if (!*mode) - *mode = g_strdup ("datagram"); - once_more = !check_infiniband_mode (mode, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*mode); - } - } while (once_more); - } - if (!*parent) { - do { - *parent = nmc_readline (_("Parent interface [none]: ")); - once_more = !check_infiniband_parent (*parent, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*parent); - } - } while (once_more); - } - if (!*p_key) { - do { - *p_key = nmc_readline (_("P_KEY [none]: ")); - once_more = !check_infiniband_p_key (*p_key, NULL, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*p_key); - } - /* If parent is specified, so has to be P_KEY */ - if (!once_more && *parent && !*p_key) { - once_more = TRUE; - g_print (_("Error: 'p-key' is mandatory when 'parent' is specified.\n")); - } - } while (once_more); - } -} - -#define WORD_INFRA "infrastructure" -#define WORD_AP "ap" -#define WORD_ADHOC "adhoc" -#define PROMPT_WIFI_MODE "(" WORD_INFRA "/" WORD_AP "/" WORD_ADHOC ") [" WORD_INFRA "]: " -static void -do_questionnaire_wifi (char **mtu, char **mac, char **cloned_mac, char **mode) -{ - gboolean once_more; - GError *error = NULL; - - /* Ask for optional arguments */ - if (!want_provide_opt_args (_("Wi-Fi"), 4)) + /* Set a sensible bond/team/bridge interface name by default */ + if (g_strcmp0 (con_type, NM_SETTING_BOND_SETTING_NAME) == 0) + ifname = unique_master_iface_ifname (nmc->connections, "nm-bond"); + else if (g_strcmp0 (con_type, NM_SETTING_TEAM_SETTING_NAME) == 0) + ifname = unique_master_iface_ifname (nmc->connections, "nm-team"); + else if (g_strcmp0 (con_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0) + ifname = unique_master_iface_ifname (nmc->connections, "nm-bridge"); + else return; - /* Most optional Wi-Fi arguments are the same as for ethernet. */ - do_questionnaire_ethernet (FALSE, mtu, mac, cloned_mac); - - if (!*mode) { - do { - *mode = nmc_readline (_("Mode %s"), PROMPT_WIFI_MODE); - if (!*mode) - *mode = g_strdup ("infrastructure"); - once_more = !check_wifi_mode (mode, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*mode); - } - } while (once_more); - } + g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, NULL); + g_free (ifname); } -static void -do_questionnaire_wimax (char **mac) -{ - gboolean once_more; - GError *error = NULL; - - /* Ask for optional 'wimax' arguments. */ - if (!want_provide_opt_args (_("WiMAX"), 1)) - return; +/*----------------------------------------------------------------------------*/ - if (!*mac) { - do { - *mac = nmc_readline (_("MAC [none]: ")); - once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*mac); - } - } while (once_more); - } -} +static OptionInfo option_info[]; +/* + * Mark options in option_info as relevant. + * The questionnaire (for --ask) will ask for them. + */ static void -do_questionnaire_pppoe (gboolean echo, char **password, char **service, char **mtu, char **mac) +enable_options (const gchar *setting_name, const gchar *property, const gchar * const *opts) { - gboolean once_more; - GError *error = NULL; - - /* Ask for optional 'pppoe' arguments. */ - if (!want_provide_opt_args (_("PPPoE"), 4)) - return; + OptionInfo *candidate; - if (!*password) - *password = nmc_readline_echo (echo, _("Password [none]: ")); - if (!*service) - *service = nmc_readline (_("Service [none]: ")); - - if (!*mtu) { - do { - *mtu = nmc_readline (_("MTU [auto]: ")); - once_more = !check_and_convert_mtu (*mtu, NULL, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*mtu); - } - } while (once_more); - } - if (!*mac) { - do { - *mac = nmc_readline (_("MAC [none]: ")); - once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*mac); - } - } while (once_more); + for (candidate = option_info; candidate->setting_name; candidate++) { + if ( strcmp (candidate->setting_name, setting_name) == 0 + && strcmp (candidate->property, property) == 0 + && (candidate->flags & OPTION_DONT_ASK) + && candidate->option + && g_strv_contains (opts, candidate->option)) { + candidate->flags |= OPTION_ENABLED; + } } } +/* + * Mark options in option_info as irrelevant (because we learned they make no sense + * or they have been set via different means). + * The questionnaire (for --ask) will not ask for them. + */ static void -do_questionnaire_mobile (gboolean echo, char **user, char **password) -{ - /* Ask for optional 'gsm' or 'cdma' arguments. */ - if (!want_provide_opt_args (_("mobile broadband"), 2)) - return; - - if (!*user) - *user = nmc_readline (_("Username [none]: ")); - if (!*password) - *password = nmc_readline_echo (echo, _("Password [none]: ")); -} - -#define WORD_PANU "panu" -#define WORD_DUN_GSM "dun-gsm" -#define WORD_DUN_CDMA "dun-cdma" -#define PROMPT_BT_TYPE "(" WORD_PANU "/" WORD_DUN_GSM "/" WORD_DUN_CDMA ") [" WORD_PANU "]: " -static void -do_questionnaire_bluetooth (char **bt_type) +disable_options (const gchar *setting_name, const gchar *property) { - gboolean once_more; - - /* Ask for optional 'bluetooth' arguments. */ - if (!want_provide_opt_args (_("bluetooth"), 1)) - return; + OptionInfo *candidate; - if (!*bt_type) { - const char *types[] = { "dun", "dun-gsm", "dun-cdma", "panu", NULL }; - const char *tmp; - do { - *bt_type = nmc_readline (_("Bluetooth type %s"), PROMPT_BT_TYPE); - if (!*bt_type) - *bt_type = g_strdup ("panu"); - tmp = nmc_string_is_valid (*bt_type, types, NULL); - once_more = !tmp; - if (once_more) { - g_print (_("Error: 'bt-type': '%s' is not a valid bluetooth type.\n"), *bt_type); - g_free (*bt_type); - } - } while (once_more); - g_free (*bt_type); - *bt_type = g_strdup (tmp); + for (candidate = option_info; candidate->setting_name; candidate++) { + if ( strcmp (candidate->setting_name, setting_name) == 0 + && (!property || strcmp (candidate->property, property) == 0)) + candidate->flags |= OPTION_DISABLED; } } +/* + * Reset marks done with enable_options() and disable_options(). + * Ensures correct operation in case more than one connection is added in a single + * nmcli session. + */ static void -do_questionnaire_vlan (char **mtu, char **flags, char **ingress, char **egress) +reset_options (void) { - gboolean once_more; - GError *error = NULL; + OptionInfo *candidate; - /* Ask for optional 'vlan' arguments. */ - if (!want_provide_opt_args (_("VLAN"), 4)) - return; - - if (!*mtu) { - do { - *mtu = nmc_readline (_("MTU [auto]: ")); - once_more = !check_and_convert_mtu (*mtu, NULL, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*mtu); - } - } while (once_more); - } - if (!*flags) { - do { - *flags = nmc_readline (_("VLAN flags (<0-7>) [none]: ")); - once_more = !check_and_convert_vlan_flags (*flags, NULL, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*flags); - } - } while (once_more); - } - if (!*ingress) { - do { - *ingress = nmc_readline (_("Ingress priority maps [none]: ")); - once_more = !check_and_convert_vlan_prio_maps (*ingress, NM_VLAN_INGRESS_MAP, NULL, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*ingress); - } - } while (once_more); - } - if (!*egress) { - do { - *egress = nmc_readline (_("Egress priority maps [none]: ")); - once_more = !check_and_convert_vlan_prio_maps (*egress, NM_VLAN_EGRESS_MAP, NULL, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*egress); - } - } while (once_more); + for (candidate = option_info; candidate->setting_name; candidate++) { + candidate->flags &= ~OPTION_DISABLED; + candidate->flags &= ~OPTION_ENABLED; } } -#define PROMPT_BOND_MODE _("Bonding mode [balance-rr]: ") -#define WORD_MIIMON "miimon" -#define WORD_ARP "arp" -#define PROMPT_BOND_MON_MODE "(" WORD_MIIMON "/" WORD_ARP ") [" WORD_MIIMON "]: " -static void -do_questionnaire_bond (char **mode, char **primary, char **miimon, - char **downdelay, char **updelay, - char **arpinterval, char **arpiptarget, - char **lacp_rate) -{ - char *monitor_mode; - unsigned long tmp; - gboolean once_more; - GError *error = NULL; - - /* Ask for optional 'bond' arguments. */ - if (!want_provide_opt_args (_("bond"), 5)) - return; +static gboolean +set_property (NMConnection *connection, + const char *setting_name, const char *property, const char *value, + char modifier, GError **error) +{ + gs_free char *property_name = NULL, *value_free = NULL; + NMSetting *setting; + GError *local = NULL; - if (!*mode) { - const char *mode_tmp; - do { - *mode = nmc_readline (PROMPT_BOND_MODE); - if (!*mode) - *mode = g_strdup ("balance-rr"); - mode_tmp = nmc_bond_validate_mode (*mode, &error); - g_free (*mode); - if (mode_tmp) { - *mode = g_strdup (mode_tmp); - } else { - g_print ("%s\n", error->message); - g_clear_error (&error); - } - } while (!mode_tmp); + setting = nm_connection_get_setting_by_name (connection, setting_name); + if (!setting) { + setting = nmc_setting_new_for_name (setting_name); + if (!setting) { + /* This should really not happen */ + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_UNKNOWN, + _("Error: don't know how to create '%s' setting."), + setting_name); + return FALSE; + } + nmc_setting_custom_init (setting); + nm_connection_add_setting (connection, setting); } - if (g_strcmp0 (*mode, "active-backup") == 0 && !*primary) { - do { - *primary = nmc_readline (_("Bonding primary interface [none]: ")); - once_more = *primary && !nm_utils_iface_valid_name (*primary); - if (once_more) { - g_print (_("Error: 'primary': '%s' is not a valid interface name.\n"), - *primary); - g_free (*primary); - } - } while (once_more); + property_name = is_property_valid (setting, property, &local); + if (!property_name) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: invalid property '%s': %s."), + property, local->message); + g_clear_error (&local); + return FALSE; } - do { - monitor_mode = nmc_readline (_("Bonding monitoring mode %s"), PROMPT_BOND_MON_MODE); - if (!monitor_mode) - monitor_mode = g_strdup (WORD_MIIMON); - g_strstrip (monitor_mode); - once_more = matches (monitor_mode, WORD_MIIMON) != 0 && matches (monitor_mode, WORD_ARP) != 0; - if (once_more) { - g_print (_("Error: '%s' is not a valid monitoring mode; use '%s' or '%s'.\n"), - monitor_mode, WORD_MIIMON, WORD_ARP); - g_free (monitor_mode); - } - } while (once_more); - - if (matches (monitor_mode, WORD_MIIMON) == 0) { - if (!*miimon) { - do { - *miimon = nmc_readline (_("Bonding miimon [100]: ")); - once_more = *miimon && !nmc_string_to_uint (*miimon, TRUE, 0, G_MAXUINT32, &tmp); - if (once_more) { - g_print (_("Error: 'miimon': '%s' is not a valid number <0-%u>.\n"), - *miimon, G_MAXUINT32); - g_free (*miimon); - } - } while (once_more); - } - if (!*downdelay) { - do { - *downdelay = nmc_readline (_("Bonding downdelay [0]: ")); - once_more = *downdelay && !nmc_string_to_uint (*downdelay, TRUE, 0, G_MAXUINT32, &tmp); - if (once_more) { - g_print (_("Error: 'downdelay': '%s' is not a valid number <0-%u>.\n"), - *downdelay, G_MAXUINT32); - g_free (*downdelay); - } - } while (once_more); + if (modifier != '-') { + /* Set/add value */ + if (modifier != '+') { + /* We allow the existing property value to be passed as parameter, + * so make a copy if we are going to free it. + */ + value = value_free = g_strdup (value); + nmc_setting_reset_property (setting, property_name, NULL); } - if (!*updelay) { - do { - *updelay = nmc_readline (_("Bonding updelay [0]: ")); - once_more = *updelay && !nmc_string_to_uint (*updelay, TRUE, 0, G_MAXUINT32, &tmp); - if (once_more) { - g_print (_("Error: 'updelay': '%s' is not a valid number <0-%u>.\n"), - *updelay, G_MAXUINT32); - g_free (*updelay); - } - } while (once_more); + if (!nmc_setting_set_property (setting, property_name, value, &local)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: failed to modify %s.%s: %s."), + setting_name, property, local->message); + g_clear_error (&local); + return FALSE; } } else { - if (!*arpinterval) { - do { - *arpinterval = nmc_readline (_("Bonding arp-interval [0]: ")); - once_more = *arpinterval && !nmc_string_to_uint (*arpinterval, TRUE, 0, G_MAXUINT32, &tmp); - if (once_more) { - g_print (_("Error: 'arp-interval': '%s' is not a valid number <0-%u>.\n"), - *arpinterval, G_MAXUINT32); - g_free (*arpinterval); - } - } while (once_more); - } - if (!*arpiptarget) { - //FIXME: verify the string - *arpiptarget = nmc_readline (_("Bonding arp-ip-target [none]: ")); - } - } - - if ( !*lacp_rate - && (g_strcmp0 (*mode, "802.3ad") == 0 || g_strcmp0 (*mode, "4") == 0)) { - do { - *lacp_rate = nmc_readline (_("LACP rate ('slow' or 'fast') [slow]: ")); - once_more = *lacp_rate && (strcmp (*lacp_rate, "slow") && - strcmp (*lacp_rate, "0") && - strcmp (*lacp_rate, "fast") && - strcmp (*lacp_rate, "1")); - if (once_more) { - printf (_("Error: 'lacp_rate': '%s' is invalid ('slow' or 'fast').\n"), - *lacp_rate); - g_free (*lacp_rate); + /* Remove value + * - either empty: remove whole value + * - or specified by index <0-n>: remove item at the index + * - or option name: remove item with the option name + */ + if (value) { + unsigned long idx; + if (nmc_string_to_uint (value, TRUE, 0, G_MAXUINT32, &idx)) + nmc_setting_remove_property_option (setting, property_name, NULL, idx, &local); + else + nmc_setting_remove_property_option (setting, property_name, value, 0, &local); + if (local) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: failed to remove a value from %s.%s: %s."), + setting_name, property, local->message); + g_clear_error (&local); + return FALSE; } - } while (once_more); + } else + nmc_setting_reset_property (setting, property_name, NULL); } - g_free (monitor_mode); -} - -static void -do_questionnaire_team_common (const char *type_name, char **config) -{ - gboolean once_more; - char *json = NULL; - GError *error = NULL; + /* Don't ask for this property in interactive mode. */ + disable_options (setting_name, property_name); - /* Ask for optional arguments. */ - if (!want_provide_opt_args (type_name, 1)) - return; + return TRUE; +} - if (!*config) { - do { - *config = nmc_readline (_("Team JSON configuration [none]: ")); - once_more = !nmc_team_check_config (*config, &json, &error); - if (once_more) { - g_print ("Error: %s\n", error->message); - g_clear_error (&error); - g_free (*config); - } - } while (once_more); +static gboolean +set_option (NmCli *nmc, NMConnection *connection, OptionInfo *option, const gchar *value, GError **error) +{ + option->flags |= OPTION_DISABLED; + if (option->check_and_set) { + return option->check_and_set (nmc, connection, option, value, error); + } else if (value) { + return set_property (connection, option->setting_name, option->property, + value, option->flags & OPTION_MULTI ? '+' : '\0', error); + } else if (option->flags & OPTION_REQD) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s' is mandatory."), option->option); + return FALSE; } - *config = json; -} - -/* Both team and team-slave curently have just ithe same one optional argument */ -static void -do_questionnaire_team (char **config) -{ - do_questionnaire_team_common (_("team"), config); + return TRUE; } -static void -do_questionnaire_team_slave (char **config) +/* + * Return relevant NameItem[] tables for given connection (based on connection type + * and slave type. + */ +static gboolean +con_settings (NMConnection *connection, const NameItem **type_settings, const NameItem **slv_settings, GError **error) { - do_questionnaire_team_common (_("team-slave"), config); -} + const char *con_type; + gs_free char *slv_type = NULL; + NMSettingConnection *s_con; -static void -do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay, char **hello_time, - char **max_age, char **ageing_time, char **mcast_snoop, char **mac) -{ - unsigned long tmp; - gboolean once_more; - GError *error = NULL; + g_return_val_if_fail (type_settings, FALSE); + g_return_val_if_fail (slv_settings, FALSE); - /* Ask for optional 'bridge' arguments. */ - if (!want_provide_opt_args (_("bridge"), 8)) - return; + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); - if (!*stp) { - gboolean stp_bool; - do { - *stp = nmc_readline (_("Enable STP %s"), prompt_yes_no (TRUE, ":")); - *stp = *stp ? *stp : g_strdup ("yes"); - normalize_yes_no (stp); - once_more = !nmc_string_to_bool (*stp, &stp_bool, &error); - if (once_more) { - g_print (_("Error: 'stp': %s.\n"), error->message); - g_clear_error (&error); - g_free (*stp); - } - } while (once_more); - } - if (!*priority) { - do { - *priority = nmc_readline (_("STP priority [32768]: ")); - *priority = *priority ? *priority : g_strdup ("32768"); - once_more = !nmc_string_to_uint (*priority, TRUE, 0, G_MAXUINT16, &tmp); - if (once_more) { - g_print (_("Error: 'priority': '%s' is not a valid number <0-%d>.\n"), - *priority, G_MAXUINT16); - g_free (*priority); - } - } while (once_more); - } - if (!*fwd_delay) { - do { - *fwd_delay = nmc_readline (_("Forward delay [15]: ")); - *fwd_delay = *fwd_delay ? *fwd_delay : g_strdup ("15"); - once_more = !nmc_string_to_uint (*fwd_delay, TRUE, 2, 30, &tmp); - if (once_more) { - g_print (_("Error: 'forward-delay': '%s' is not a valid number <2-30>.\n"), - *fwd_delay); - g_free (*fwd_delay); - } - } while (once_more); + con_type = nm_setting_connection_get_slave_type (s_con); + if (!con_type) + con_type = "no"; + slv_type = g_strdup_printf ("%s-slave", con_type); + if (slv_type) { + *slv_settings = get_valid_settings_array (slv_type); + if (!*slv_settings) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: invalid slave type; %s."), slv_type); + return FALSE; + } + } else { + *slv_settings = NULL; } - if (!*hello_time) { - do { - *hello_time = nmc_readline (_("Hello time [2]: ")); - *hello_time = *hello_time ? *hello_time : g_strdup ("2"); - once_more = !nmc_string_to_uint (*hello_time, TRUE, 1, 10, &tmp); - if (once_more) { - g_print (_("Error: 'hello-time': '%s' is not a valid number <1-10>.\n"), - *hello_time); - g_free (*hello_time); - } - } while (once_more); - } - if (!*max_age) { - do { - *max_age = nmc_readline (_("Max age [20]: ")); - *max_age = *max_age ? *max_age : g_strdup ("20"); - once_more = !nmc_string_to_uint (*max_age, TRUE, 6, 40, &tmp); - if (once_more) { - g_print (_("Error: 'max-age': '%s' is not a valid number <6-40>.\n"), - *max_age); - g_free (*max_age); - } - } while (once_more); - } - if (!*ageing_time) { - do { - *ageing_time = nmc_readline (_("MAC address ageing time [300]: ")); - *ageing_time = *ageing_time ? *ageing_time : g_strdup ("300"); - once_more = !nmc_string_to_uint (*ageing_time, TRUE, 0, 1000000, &tmp); - if (once_more) { - g_print (_("Error: 'ageing-time': '%s' is not a valid number <0-1000000>.\n"), - *ageing_time); - g_free (*ageing_time); - } - } while (once_more); - } - if (!*mcast_snoop) { - gboolean mcast_snoop_bool; - do { - *mcast_snoop = nmc_readline (_("Enable IGMP snooping %s"), prompt_yes_no (TRUE, ":")); - *mcast_snoop = *mcast_snoop ? *mcast_snoop : g_strdup ("yes"); - normalize_yes_no (mcast_snoop); - once_more = !nmc_string_to_bool (*mcast_snoop, &mcast_snoop_bool, &error); - if (once_more) { - g_print (_("Error: 'multicast-snooping': %s.\n"), error->message); - g_clear_error (&error); - g_free (*mcast_snoop); - } - } while (once_more); - } - if (!*mac) { - do { - *mac = nmc_get_user_input (_("MAC [none]: ")); - once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*mac); - } - } while (once_more); + con_type = nm_setting_connection_get_connection_type (s_con); + if (!con_type) + con_type = NM_SETTING_GENERIC_SETTING_NAME; + *type_settings = get_valid_settings_array (con_type); + if (!*type_settings) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: invalid connection type; %s."), con_type); + return FALSE; } + + return TRUE; } +/* + * Make sure all required settings are in place (should be called when + * it's possible that a type is aready set). + */ static void -do_questionnaire_bridge_slave (char **priority, char **path_cost, char **hairpin) +ensure_settings (NMConnection *connection, const NameItem *item) { - unsigned long tmp; - gboolean once_more; - GError *error = NULL; - - /* Ask for optional 'bridge-slave' arguments. */ - if (!want_provide_opt_args (_("bridge-slave"), 3)) - return; + const NameItem *setting_item; + NMSetting *setting; - if (!*priority) { - do { - *priority = nmc_readline (_("Bridge port priority [32]: ")); - *priority = *priority ? *priority : g_strdup ("32"); - once_more = !bridge_prop_string_to_uint (*priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT, - NM_SETTING_BRIDGE_PORT_PRIORITY, &tmp, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*priority); - } - } while (once_more); - } - if (!*path_cost) { - do { - *path_cost = nmc_readline (_("Bridge port STP path cost [100]: ")); - *path_cost = *path_cost ? *path_cost : g_strdup ("100"); - once_more = !bridge_prop_string_to_uint (*path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT, - NM_SETTING_BRIDGE_PORT_PATH_COST, &tmp, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*path_cost); - } - } while (once_more); - } - if (!*hairpin) { - gboolean hairpin_bool; - do { - *hairpin = nmc_readline (_("Hairpin %s"), prompt_yes_no (TRUE, ":")); - *hairpin = *hairpin ? *hairpin : g_strdup ("yes"); - normalize_yes_no (hairpin); - once_more = !nmc_string_to_bool (*hairpin, &hairpin_bool, &error); - if (once_more) { - g_print (_("Error: 'hairpin': %s.\n"), error->message); - g_clear_error (&error); - g_free (*hairpin); - } - } while (once_more); + for (setting_item = item; setting_item->name; setting_item++) { + if (!setting_item->mandatory) + continue; + if (nm_connection_get_setting_by_name (connection, setting_item->name)) + continue; + setting = nmc_setting_new_for_name (setting_item->name); + if (setting) { + nmc_setting_custom_init (setting); + nm_connection_add_setting (connection, setting); + } } } -static void -do_questionnaire_vpn (char **user) -{ - /* Ask for optional 'vpn' arguments. */ - if (!want_provide_opt_args (_("VPN"), 1)) - return; +/*----------------------------------------------------------------------------*/ - if (!*user) - *user = nmc_readline (_("Username [none]: ")); -} -static void -do_questionnaire_olpc (char **channel, char **dhcp_anycast) +static char * +gen_func_vpn_types (const char *text, int state) { - unsigned long tmp; - gboolean once_more; - GError *error = NULL; - - /* Ask for optional 'olpc' arguments. */ - if (!want_provide_opt_args (_("OLPC Mesh"), 2)) - return; + gs_strfreev char **plugin_names = NULL; - if (!*channel) { - do { - *channel = nmc_readline (_("OLPC Mesh channel [1]: ")); - once_more = *channel && !nmc_string_to_uint (*channel, TRUE, 1, 13, &tmp); - if (once_more) { - g_print (_("Error: 'channel': '%s' is not a valid number <1-13>.\n"), - *channel); - g_free (*channel); - } - } while (once_more); - } - if (!*dhcp_anycast) { - do { - *dhcp_anycast = nmc_readline (_("DHCP anycast MAC address [none]: ")); - once_more = !check_mac (*dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*dhcp_anycast); - } - } while (once_more); - } + plugin_names = nm_vpn_plugin_info_list_get_service_types (nm_vpn_get_plugin_infos (), FALSE, TRUE); + return nmc_rl_gen_func_basic (text, state, (const char **) plugin_names); } -#define PROMPT_ADSL_ENCAP "(" NM_SETTING_ADSL_ENCAPSULATION_VCMUX "/" NM_SETTING_ADSL_ENCAPSULATION_LLC ") [none]: " -static void -do_questionnaire_adsl (gboolean echo, char **password, char **encapsulation) +static char * +gen_func_bool_values_l10n (const char *text, int state) { - gboolean once_more; - GError *error = NULL; - - /* Ask for optional 'adsl' arguments. */ - if (!want_provide_opt_args (_("ADSL"), 2)) - return; - - if (!*password) - *password = nmc_readline_echo (echo, _("Password [none]: ")); - - if (!*encapsulation) { - do { - *encapsulation = nmc_readline (_("ADSL encapsulation %s"), PROMPT_ADSL_ENCAP); - once_more = !check_adsl_encapsulation (encapsulation, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*encapsulation); - } - } while (once_more); - } + const char *words[] = { WORD_LOC_YES, WORD_LOC_NO, NULL }; + return nmc_rl_gen_func_basic (text, state, words); } -static void -do_questionnaire_macvlan (char **tap) +static char * +gen_func_wifi_mode (const char *text, int state) { - gboolean once_more; - GError *error = NULL; - - /* Ask for optional 'macvlan' arguments. */ - if (!want_provide_opt_args (_("macvlan"), 1)) - return; - - if (!*tap) { - gboolean tap_bool; - do { - *tap = nmc_readline (_("Tap %s"), prompt_yes_no (FALSE, ":")); - *tap = *tap ? *tap : g_strdup ("yes"); - normalize_yes_no (tap); - once_more = !nmc_string_to_bool (*tap, &tap_bool, &error); - if (once_more) { - g_print (_("Error: 'tap': %s.\n"), error->message); - g_clear_error (&error); - g_free (*tap); - } - } while (once_more); - } + const char *words[] = { "infrastructure", "ap", "adhoc", NULL }; + return nmc_rl_gen_func_basic (text, state, words); } -static void -do_questionnaire_vxlan (char **parent, char **local, char **src_port_min, - char **src_port_max, char **dst_port) +static char * +gen_func_ib_type (const char *text, int state) { - unsigned long tmp; - gboolean once_more; - - /* Ask for optional 'vxlan' arguments. */ - if (!want_provide_opt_args (_("VXLAN"), 5)) - return; - - if (!*parent) { - do { - *parent = nmc_readline (_("Parent device [none]: ")); - once_more = *parent - && !nm_utils_is_uuid (*parent) - && !nm_utils_iface_valid_name (*parent); - if (once_more) { - g_print (_("Error: 'dev': '%s' is neither UUID nor interface name.\n"), - *parent); - g_free (*parent); - } - } while (once_more); - } - - if (!*local) { - do { - *local = nmc_readline (_("Local address [none]: ")); - once_more = *local - && !nm_utils_ipaddr_valid (AF_INET, *local) - && !nm_utils_ipaddr_valid (AF_INET6, *local); - if (once_more) { - g_print (_("Error: 'local': '%s' is not a valid IP address.\n"), - *local); - g_free (*local); - } - } while (once_more); - } - - if (!*src_port_min) { - do { - *src_port_min = nmc_readline (_("Minimum source port [0]: ")); - *src_port_min = *src_port_min ? *src_port_min : g_strdup ("0"); - once_more = !nmc_string_to_uint (*src_port_min, TRUE, 0, 65535, &tmp); - if (once_more) { - g_print (_("Error: 'source-port-min': '%s' is not a valid number <0-65535>.\n"), - *src_port_min); - g_free (*src_port_min); - } - } while (once_more); - } - - if (!*src_port_max) { - do { - *src_port_max = nmc_readline (_("Maximum source port [0]: ")); - *src_port_max = *src_port_max ? *src_port_max : g_strdup ("0"); - once_more = !nmc_string_to_uint (*src_port_max, TRUE, 0, 65535, &tmp); - if (once_more) { - g_print (_("Error: 'source-port-max': '%s' is not a valid number <0-65535>.\n"), - *src_port_max); - g_free (*src_port_max); - } - } while (once_more); - } - - if (!*dst_port) { - do { - *dst_port = nmc_readline (_("Destination port [8472]: ")); - *dst_port = *dst_port ? *dst_port : g_strdup ("8472"); - once_more = !nmc_string_to_uint (*dst_port, TRUE, 0, 65535, &tmp); - if (once_more) { - g_print (_("Error: 'destination-port': '%s' is not a valid number <0-65535>.\n"), - *dst_port); - g_free (*dst_port); - } - } while (once_more); - } + const char *words[] = { "datagram", "connected", NULL }; + return nmc_rl_gen_func_basic (text, state, words); } -static gboolean -split_address (char* str, char **ip, char **rest) +static char * +gen_func_bt_type (const char *text, int state) { - size_t n1, n2, n3; - - *ip = *rest = NULL; - if (!str) - return FALSE; - - n1 = strspn (str, " \t"); - n2 = strcspn (str+n1, " \t\0") + n1; - n3 = strspn (str+n2, " \t") + n2; - - str[n2] = '\0'; - *ip = str[n1] ? str + n1 : NULL; - *rest = str[n3] ? str + n3 : NULL; - - return TRUE; + const char *words[] = { "panu", "dun-gsm", "dun-cdma", NULL }; + return nmc_rl_gen_func_basic (text, state, words); } -static void -ask_for_ip_addresses (NMConnection *connection, int family) +static char * +gen_func_bond_mode (const char *text, int state) { - gboolean ip_loop; - GError *error = NULL; - char *str, *ip, *rest; - const char *prompt; - gboolean added; - NMIPAddress *ipaddr; - - if (family == AF_INET) - prompt =_("IPv4 address (IP[/plen]) [none]: "); - else - prompt =_("IPv6 address (IP[/plen]) [none]: "); - - ip_loop = TRUE; - do { - str = nmc_readline ("%s", prompt); - split_address (str, &ip, &rest); - if (ip) { - ipaddr = nmc_parse_and_build_address (family, ip, &error); - if (ipaddr) { - if (family == AF_INET) - added = add_ip4_address_to_connection (ipaddr, connection); - else - added = add_ip6_address_to_connection (ipaddr, connection); - if (added) - g_print (_(" Address successfully added: %s\n"), ip); - else - g_print (_(" Warning: address already present: %s\n"), ip); - if (rest) - g_print (_(" Warning: ignoring garbage at the end: '%s'\n"), rest); - } else { - g_prefix_error (&error, _("Error: ")); - g_print ("%s\n", error->message); - g_clear_error (&error); - } - } else - ip_loop = FALSE; - - g_free (str); - } while (ip_loop); + const char *words[] = { "balance-rr", "active-backup", "balance-xor", "broadcast", + "802.3ad", "balance-tlb", "balance-alb", NULL }; + return nmc_rl_gen_func_basic (text, state, words); } - -static void -maybe_ask_for_gateway (NMConnection *connection, int family) +static char * +gen_func_bond_mon_mode (const char *text, int state) { - gboolean gw_loop; - char *str, *gw, *rest; - const char *prompt; - NMSettingIPConfig *s_ip; - - if (family == AF_INET) { - prompt =_("IPv4 gateway [none]: "); - s_ip = nm_connection_get_setting_ip4_config (connection); - } else { - prompt =_("IPv6 gateway [none]: "); - s_ip = nm_connection_get_setting_ip6_config (connection); - } - if (s_ip == NULL) - return; - if ( nm_setting_ip_config_get_num_addresses (s_ip) == 0 - || nm_setting_ip_config_get_gateway (s_ip) != NULL) - return; - - gw_loop = TRUE; - do { - str = nmc_readline ("%s", prompt); - split_address (str, &gw, &rest); - if (gw) { - if (nm_utils_ipaddr_valid (family, gw)) { - g_object_set (s_ip, - NM_SETTING_IP_CONFIG_GATEWAY, gw, - NULL); - gw_loop = FALSE; - } else - g_print (_("Error: invalid gateway address '%s'\n"), gw); - } else - gw_loop = FALSE; - g_free (str); - } while (gw_loop); + const char *words[] = { "miimon", "arp", NULL }; + return nmc_rl_gen_func_basic (text, state, words); } -static void -do_questionnaire_ip (NMConnection *connection) +static char * +gen_func_adsl_proto (const char *text, int state) { - char *answer; - - /* Ask for IP addresses */ - answer = nmc_readline (_("Do you want to add IP addresses? %s"), prompt_yes_no (TRUE, NULL)); - answer = answer ? g_strstrip (answer) : NULL; - if (answer && matches (answer, WORD_LOC_YES) != 0) { - g_free (answer); - return; - } - g_free (answer); - - g_print (_("Press <Enter> to finish adding addresses.\n")); - - ask_for_ip_addresses (connection, AF_INET); - maybe_ask_for_gateway (connection, AF_INET); - ask_for_ip_addresses (connection, AF_INET6); - maybe_ask_for_gateway (connection, AF_INET6); + const char *words[] = { "pppoe", "pppoa", "ipoatm", NULL }; + return nmc_rl_gen_func_basic (text, state, words); } -static NMSetting * -is_setting_valid (NMConnection *connection, const NameItem *valid_settings_main, const NameItem *valid_settings_slave, char *setting) +static char * +gen_func_adsl_encap (const char *text, int state) { - const char *setting_name; - - if (!(setting_name = check_valid_name (setting, valid_settings_main, valid_settings_slave, NULL))) - return NULL; - return nm_connection_get_setting_by_name (connection, setting_name); + const char *words[] = { "vcmux", "llc", NULL }; + return nmc_rl_gen_func_basic (text, state, words); } static char * -is_property_valid (NMSetting *setting, const char *property, GError **error) +gen_func_tun_mode (const char *text, int state) { - char **valid_props = NULL; - const char *prop_name; - char *ret; - - valid_props = nmc_setting_get_valid_properties (setting); - prop_name = nmc_string_is_valid (property, (const char **) valid_props, error); - ret = g_strdup (prop_name); - g_strfreev (valid_props); - return ret; + const char *words[] = { "tun", "tap", NULL }; + return nmc_rl_gen_func_basic (text, state, words); } -#define WORD_TUN "tun" -#define WORD_TAP "tap" -#define PROMPT_TUN_MODE "(" WORD_TUN "/" WORD_TAP ") [" WORD_TUN "]: " -static void -do_questionnaire_tun (char **user, char **group, - char **pi, char **vnet_hdr, char **multi_queue) +static char * +gen_func_ip_tunnel_mode (const char *text, int state) { - gboolean once_more; - GError *error = NULL; - gboolean b; - - /* Ask for optional 'tun' arguments. */ - if (!want_provide_opt_args (_("Tun"), 5)) - return; - - if (!*user) { - do { - *user = nmc_readline (_("User ID [none]: ")); - if (!*user) - break; - once_more = !check_user_group_id (*user, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*user); - } - } while (once_more); - } - if (!*group) { - do { - *group = nmc_readline (_("Group ID [none]: ")); - if (!*group) - break; - once_more = !check_user_group_id (*group, &error); - if (once_more) { - g_print ("%s\n", error->message); - g_clear_error (&error); - g_free (*group); - } - } while (once_more); - } + gs_free const char **words = NULL; - if (!*pi) { - do { - *pi = nmc_readline (_("Enable PI %s"), prompt_yes_no (FALSE, ":")); - *pi = *pi ? *pi : g_strdup ("no"); - normalize_yes_no (pi); - once_more = !nmc_string_to_bool (*pi, &b, &error); - if (once_more) { - g_print (_("Error: 'pi': %s.\n"), error->message); - g_clear_error (&error); - g_free (*pi); - } - } while (once_more); - } - if (!*vnet_hdr) { - do { - *vnet_hdr = nmc_readline (_("Enable VNET header %s"), prompt_yes_no (FALSE, ":")); - *vnet_hdr = *vnet_hdr ? *vnet_hdr : g_strdup ("no"); - normalize_yes_no (vnet_hdr); - once_more = !nmc_string_to_bool (*vnet_hdr, &b, &error); - if (once_more) { - g_print (_("Error: 'vnet-hdr': %s.\n"), error->message); - g_clear_error (&error); - g_free (*vnet_hdr); - } - } while (once_more); - } - if (!*multi_queue) { - do { - *multi_queue = nmc_readline (_("Enable multi queue %s"), prompt_yes_no (FALSE, ":")); - *multi_queue = *multi_queue ? *multi_queue : g_strdup ("no"); - normalize_yes_no (multi_queue); - once_more = !nmc_string_to_bool (*multi_queue, &b, &error); - if (once_more) { - g_print (_("Error: 'multi-queue': %s.\n"), error->message); - g_clear_error (&error); - g_free (*multi_queue); - } - } while (once_more); - } + words = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (), + NM_IP_TUNNEL_MODE_UNKNOWN + 1, + G_MAXINT); + return nmc_rl_gen_func_basic (text, state, words); } -static void -do_questionnaire_ip_tunnel (char **local, char **parent) +static char * +gen_func_macvlan_mode (const char *text, int state) { - gboolean once_more; - - /* Ask for optional 'ip-tunnel' arguments. */ - if (!want_provide_opt_args (_("IP Tunnel"), 2)) - return; - - if (!*local) { - do { - *local = nmc_readline (_("Local endpoint [none]: ")); - if (!*local) - break; - once_more = !nm_utils_ipaddr_valid (AF_INET, *local) - && !nm_utils_ipaddr_valid (AF_INET6, *local); - if (once_more) { - g_print (_("Error: 'local': '%s' is not valid; must be an IP address\n"), - *local); - g_free (*local); - } - } while (once_more); - } + gs_free const char **words = NULL; - if (!*parent) { - do { - *parent = nmc_readline (_("Parent device [none]: ")); - once_more = *parent - && !nm_utils_is_uuid (*parent) - && !nm_utils_iface_valid_name (*parent); - if (once_more) { - g_print (_("Error: 'dev': '%s' is neither UUID nor interface name.\n"), - *parent); - g_free (*parent); - } - } while (once_more); - } + words = nm_utils_enum_get_values (nm_setting_macvlan_mode_get_type(), + NM_SETTING_MACVLAN_MODE_UNKNOWN + 1, + G_MAXINT); + return nmc_rl_gen_func_basic (text, state, words); } -static gboolean -read_connection_properties (NMConnection *connection, - int argc, - char **argv, - GError **error) +static char * +gen_func_master_ifnames (const char *text, int state) { - NMSetting *setting; + int i; + GPtrArray *ifnames; + char *ret; + NMConnection *con; NMSettingConnection *s_con; - const char *con_type; - const char *s_dot_p; - const char *value; - char **strv = NULL; - char *slv_type = NULL; - const char *setting_name; - gboolean append = FALSE; - gboolean remove = FALSE; - gboolean success = FALSE; - GError *local = NULL; + const char *con_type, *ifname; - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); + if (!nm_cli.connections) + return NULL; - /* First check if we have a slave-type, as this would mean we will not - * have ip properties but possibly others, slave-type specific. - */ - con_type = nm_setting_connection_get_slave_type (s_con); - if (!con_type) - con_type = "no"; + /* Disable appending space after completion */ + rl_completion_append_character = '\0'; - slv_type = g_strdup_printf ("%s-slave", con_type); + ifnames = g_ptr_array_sized_new (20); + for (i = 0; i < nm_cli.connections->len; i++) { + con = NM_CONNECTION (nm_cli.connections->pdata[i]); + s_con = nm_connection_get_setting_connection (con); + g_assert (s_con); + con_type = nm_setting_connection_get_connection_type (s_con); + if (g_strcmp0 (con_type, nmc_tab_completion.con_type) != 0) + continue; + ifname = nm_connection_get_interface_name (con); + g_ptr_array_add (ifnames, (gpointer) ifname); + } + g_ptr_array_add (ifnames, (gpointer) NULL); - con_type = nm_setting_connection_get_connection_type (s_con); + ret = nmc_rl_gen_func_basic (text, state, (const char **) ifnames->pdata); - /* Go through arguments and set properties */ - while (argc) { - gs_free char *property_name = NULL; + g_ptr_array_free (ifnames, TRUE); + return ret; +} - s_dot_p = *argv; - next_arg (&argc, &argv); - value = *argv; - next_arg (&argc, &argv); - if (!s_dot_p) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: <setting>.<property> argument is missing.")); - goto finish; - } - if (!value) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: value for '%s' is missing."), s_dot_p); - goto finish; - } - /* Empty string will reset the value to default */ - if (value[0] == '\0') - value = NULL; +/*----------------------------------------------------------------------------*/ - if (s_dot_p[0] == '+') { - s_dot_p++; - append = TRUE; - } else if (s_dot_p[0] == '-') { - s_dot_p++; - remove = TRUE; - } +static gboolean +set_connection_type (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error) +{ + const NameItem *type_settings, *slv_settings; + GError *local = NULL; + const gchar *master[] = { "master", NULL }; - strv = g_strsplit (s_dot_p, ".", 2); - if (g_strv_length (strv) != 2) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: invalid <setting>.<property> '%s'."), s_dot_p); - goto finish; - } + value = check_valid_name (value, nmc_valid_connection_types, NULL, &local); + if (!value) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: bad connection type: %s."), local->message); + g_clear_error (&local); + return FALSE; + } - setting_name = check_valid_name (strv[0], get_valid_settings_array (con_type), - get_valid_settings_array (slv_type), &local); - if (!setting_name) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: invalid or not allowed setting '%s': %s."), - strv[0], local->message); - g_clear_error (&local); - goto finish; - } - setting = nm_connection_get_setting_by_name (connection, setting_name); - if (!setting) { - setting = nmc_setting_new_for_name (setting_name); - if (!setting) { - /* This should really not happen */ - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_UNKNOWN, - _("Error: don't know how to create '%s' setting."), - setting_name); - goto finish; - } - nm_connection_add_setting (connection, setting); + if (g_strcmp0 (value, "bond-slave") == 0) { + value = NM_SETTING_WIRED_SETTING_NAME; + if (!set_property (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME, + '\0', error)) { + return FALSE; } - - property_name = is_property_valid (setting, strv[1], &local); - if (!property_name) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: invalid property '%s': %s."), - strv[1], local->message); - g_clear_error (&local); - goto finish; + enable_options (NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER, master); + } else if (g_strcmp0 (value, "bridge-slave") == 0) { + value = NM_SETTING_WIRED_SETTING_NAME; + if (!set_property (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, + '\0', error)) { + return FALSE; } - - if (!remove) { - /* Set/add value */ - if (!append) - nmc_setting_reset_property (setting, property_name, NULL); - if (!nmc_setting_set_property (setting, property_name, value, &local)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: failed to modify %s.%s: %s."), - strv[0], strv[1], local->message); - g_clear_error (&local); - goto finish; - } - } else { - /* Remove value - * - either empty: remove whole value - * - or specified by index <0-n>: remove item at the index - * - or option name: remove item with the option name - */ - if (value) { - unsigned long idx; - if (nmc_string_to_uint (value, TRUE, 0, G_MAXUINT32, &idx)) - nmc_setting_remove_property_option (setting, property_name, NULL, idx, &local); - else - nmc_setting_remove_property_option (setting, property_name, value, 0, &local); - if (local) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: failed to remove a value from %s.%s: %s."), - strv[0], strv[1], local->message); - g_clear_error (&local); - goto finish; - } - } else - nmc_setting_reset_property (setting, property_name, NULL); + enable_options (NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER, master); + } else if (g_strcmp0 (value, "team-slave") == 0) { + value = NM_SETTING_WIRED_SETTING_NAME; + if (!set_property (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, + '\0', error)) { + return FALSE; } - - g_strfreev (strv); - strv = NULL; + enable_options (NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER, master); } - success = TRUE; -finish: - if (strv) - g_strfreev (strv); - g_free (slv_type); - return success; -} - -static gboolean -complete_slave (NMSettingConnection *s_con, - const GPtrArray *all_connections, - const char *slave_type, - const char *master, - const char *type, - gboolean ask, - GError **error) -{ - char *master_ask = NULL; - const char *checked_master = NULL; - - if (type) - g_print (_("Warning: 'type' is ignored. " - "Use 'nmcli connection add \"%s\" ...' instead."), - type); - - if (nm_setting_connection_get_master (s_con)) { - /* Master already set. */ - if (master) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: redundant 'master' option.")); - return FALSE; - } - g_object_set (s_con, - NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type, - NULL); - return TRUE; + /* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */ + if ( (strcmp (value, NM_SETTING_BOND_SETTING_NAME) == 0) + || (strcmp (value, NM_SETTING_TEAM_SETTING_NAME) == 0) + || (strcmp (value, NM_SETTING_BRIDGE_SETTING_NAME) == 0) + || (strcmp (value, NM_SETTING_VLAN_SETTING_NAME) == 0)) { + disable_options (NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_INTERFACE_NAME); } - if (!master && ask) - master = master_ask = nmc_readline (PROMPT_MASTER); - if (!master) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'master' is required.")); + if (!set_property (con, option->setting_name, option->property, value, '\0', error)) return FALSE; - } - /* Verify master argument */ - checked_master = normalized_master_for_slave (all_connections, master, slave_type, NULL); - /* Change properties in 'connection' setting */ - g_object_set (s_con, - NM_SETTING_CONNECTION_MASTER, checked_master, - NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type, - NULL); + if (!con_settings (con, &type_settings, &slv_settings, error)) + return FALSE; - g_free (master_ask); + ensure_settings (con, slv_settings); + ensure_settings (con, type_settings); return TRUE; } static gboolean -complete_connection_by_type (NMConnection *connection, - const char *con_type, - const GPtrArray *all_connections, - gboolean ask, - gboolean show_secrets, - int argc, - char **argv, - GError **error) +set_connection_iface (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error) { - NMSettingConnection *s_con; - NMSettingGeneric *s_generic; - NMSettingWired *s_wired; - NMSettingInfiniband *s_infiniband; - NMSettingWireless *s_wifi; - NMSettingWimax *s_wimax; - NMSettingPppoe *s_pppoe; - NMSettingGsm *s_gsm; - NMSettingCdma *s_cdma; - NMSettingBluetooth *s_bt; - NMSettingVlan *s_vlan; - NMSettingBond *s_bond; - NMSettingTeam *s_team; - NMSettingTeamPort *s_team_port; - NMSettingBridge *s_bridge; - NMSettingBridgePort *s_bridge_port; - NMSettingVpn *s_vpn; - NMSettingOlpcMesh *s_olpc_mesh; - NMSettingAdsl *s_adsl; - NMSettingTun *s_tun; - NMSettingIPTunnel *s_ip_tunnel; - NMSettingMacvlan *s_macvlan; - NMSettingVxlan *s_vxlan; - - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - - if (!strcmp (con_type, NM_SETTING_WIRED_SETTING_NAME)) { - /* Build up the settings required for 'ethernet' */ - gboolean success = FALSE; - const char *mtu_c = NULL; - char *mtu = NULL; - guint32 mtu_int = 0; - const char *mac_c = NULL; - char *mac = NULL; - const char *cloned_mac_c = NULL; - char *cloned_mac = NULL; - nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE}, - {"mac", TRUE, &mac_c, FALSE}, - {"cloned-mac", TRUE, &cloned_mac_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; - - /* Also ask for all optional arguments if '--ask' is specified. */ - mtu = g_strdup (mtu_c); - mac = g_strdup (mac_c); - cloned_mac = g_strdup (cloned_mac_c); - if (ask) - do_questionnaire_ethernet (TRUE, &mtu, &mac, &cloned_mac); - - if (!check_and_convert_mtu (mtu, &mtu_int, error)) - goto cleanup_wired; - if (!check_mac (mac, ARPHRD_ETHER, "mac", error)) - goto cleanup_wired; - if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error)) - goto cleanup_wired; - - /* Add ethernet setting */ - s_wired = (NMSettingWired *) nm_setting_wired_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - - if (mtu) - g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL); - if (mac) - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); - if (cloned_mac) - g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_mac, NULL); - - success = TRUE; -cleanup_wired: - g_free (mtu); - g_free (mac); - g_free (cloned_mac); - if (!success) - return FALSE; - - } else if (!strcmp (con_type, NM_SETTING_INFINIBAND_SETTING_NAME)) { - /* Build up the settings required for 'infiniband' */ - gboolean success = FALSE; - const char *mtu_c = NULL; - char *mtu = NULL; - guint32 mtu_int = 0; - const char *mac_c = NULL; - char *mac = NULL; - const char *mode_c = NULL; - char *mode = NULL; - const char *parent_c = NULL; - char *parent = NULL; - const char *p_key_c = NULL; - char *p_key = NULL; - guint32 p_key_int = 0; - nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE}, - {"mac", TRUE, &mac_c, FALSE}, - {"transport-mode", TRUE, &mode_c, FALSE}, - {"parent", TRUE, &parent_c, FALSE}, - {"p-key", TRUE, &p_key_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; - - /* Also ask for all optional arguments if '--ask' is specified. */ - mtu = g_strdup (mtu_c); - mac = g_strdup (mac_c); - mode = g_strdup (mode_c); - parent = g_strdup (parent_c); - p_key = g_strdup (p_key_c); - if (ask) - do_questionnaire_infiniband (&mtu, &mac, &mode, &parent, &p_key); - - if (!check_and_convert_mtu (mtu, &mtu_int, error)) - goto cleanup_ib; - if (!check_mac (mac, ARPHRD_INFINIBAND, "mac", error)) - goto cleanup_ib; - if (!check_infiniband_mode (&mode, error)) - goto cleanup_ib; - if (p_key) { - if (!check_infiniband_p_key (p_key, &p_key_int, error)) - goto cleanup_ib; - if (!check_infiniband_parent (parent, error)) - goto cleanup_ib; - } else if (parent) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'parent': not valid without 'p-key'.")); - goto cleanup_ib; - } - - /* Add 'infiniband' setting */ - s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new (); - nm_connection_add_setting (connection, NM_SETTING (s_infiniband)); - - g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, mode ? mode : "datagram", NULL); - if (mtu) - g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu_int, NULL); - if (mac) - g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL); - if (p_key) - g_object_set (s_infiniband, NM_SETTING_INFINIBAND_P_KEY, p_key_int, NULL); - if (parent) - g_object_set (s_infiniband, NM_SETTING_INFINIBAND_PARENT, parent, NULL); - - - success = TRUE; -cleanup_ib: - g_free (mtu); - g_free (mac); - g_free (mode); - g_free (parent); - g_free (p_key); - if (!success) - return FALSE; - - } else if (!strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME)) { - /* Build up the settings required for 'wifi' */ - gboolean success = FALSE; - char *ssid_ask = NULL; - const char *ssid = NULL; - GBytes *ssid_bytes; - const char *mtu_c = NULL; - char *mtu = NULL; - guint32 mtu_int = 0; - const char *mac_c = NULL; - char *mac = NULL; - const char *cloned_mac_c = NULL; - char *cloned_mac = NULL; - const char *mode_c = NULL; - char *mode = NULL; - nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask}, - {"mtu", TRUE, &mtu_c, FALSE}, - {"mac", TRUE, &mac_c, FALSE}, - {"cloned-mac", TRUE, &cloned_mac_c, FALSE}, - {"mode", TRUE, &mode_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; - - if (!ssid && ask) - ssid = ssid_ask = nmc_readline (_("SSID: ")); - if (!ssid) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'ssid' is required.")); - return FALSE; - } - - /* Also ask for all optional arguments if '--ask' is specified. */ - mtu = g_strdup (mtu_c); - mac = g_strdup (mac_c); - cloned_mac = g_strdup (cloned_mac_c); - mode = g_strdup (mode_c); - if (ask) - do_questionnaire_wifi (&mtu, &mac, &cloned_mac, &mode); - - if (!check_and_convert_mtu (mtu, &mtu_int, error)) - goto cleanup_wifi; - if (!check_mac (mac, ARPHRD_ETHER, "mac", error)) - goto cleanup_wifi; - if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error)) - goto cleanup_wifi; - if (!check_wifi_mode (&mode, error)) - goto cleanup_wifi; - - /* Add wifi setting */ - s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - - ssid_bytes = g_bytes_new (ssid, strlen (ssid)); - g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid_bytes, NULL); - - if (mtu) - g_object_set (s_wifi, NM_SETTING_WIRELESS_MTU, mtu_int, NULL); - if (mac) - g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL); - if (cloned_mac) - g_object_set (s_wifi, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, cloned_mac, NULL); - if (mode) - g_object_set (s_wifi, NM_SETTING_WIRELESS_MODE, mode, NULL); - - g_bytes_unref (ssid_bytes); - - success = TRUE; -cleanup_wifi: - g_free (ssid_ask); - g_free (mtu); - g_free (mac); - g_free (cloned_mac); - g_free (mode); - if (!success) - return FALSE; - - } else if (!strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)) { - /* Build up the settings required for 'wimax' */ - gboolean success = FALSE; - const char *nsp_name = NULL; - char *nsp_name_ask = NULL; - const char *mac_c = NULL; - char *mac = NULL; - nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !ask}, - {"mac", TRUE, &mac_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; - - if (!nsp_name && ask) - nsp_name = nsp_name_ask = nmc_readline (_("WiMAX NSP name: ")); - if (!nsp_name) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'nsp' is required.")); - goto cleanup_wimax; - } - - /* Also ask for all optional arguments if '--ask' is specified. */ - mac = g_strdup (mac_c); - if (ask) - do_questionnaire_wimax (&mac); - - if (!check_mac (mac, ARPHRD_ETHER, "mac", error)) - goto cleanup_wimax; - - /* Add 'wimax' setting */ - s_wimax = (NMSettingWimax *) nm_setting_wimax_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wimax)); - g_object_set (s_wimax, NM_SETTING_WIMAX_NETWORK_NAME, nsp_name, NULL); - - if (mac) - g_object_set (s_wimax, NM_SETTING_WIMAX_MAC_ADDRESS, mac, NULL); - - success = TRUE; -cleanup_wimax: - g_free (nsp_name_ask); - g_free (mac); - if (!success) - return FALSE; - - } else if (!strcmp (con_type, NM_SETTING_PPPOE_SETTING_NAME)) { - /* Build up the settings required for 'pppoe' */ - gboolean success = FALSE; - const char *username = NULL; - char *username_ask = NULL; - const char *password_c = NULL; - char *password = NULL; - const char *service_c = NULL; - char *service = NULL; - const char *mtu_c = NULL; - char *mtu = NULL; - guint32 mtu_int = 0; - const char *mac_c = NULL; - char *mac = NULL; - nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask}, - {"password", TRUE, &password_c, FALSE}, - {"service", TRUE, &service_c, FALSE}, - {"mtu", TRUE, &mtu_c, FALSE}, - {"mac", TRUE, &mac_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; - - if (!username && ask) - username = username_ask = nmc_readline (_("PPPoE username: ")); - if (!username) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'username' is required.")); - goto cleanup_pppoe; - } - - /* Also ask for all optional arguments if '--ask' is specified. */ - password = g_strdup (password_c); - service = g_strdup (service_c); - mtu = g_strdup (mtu_c); - mac = g_strdup (mac_c); - if (ask) - do_questionnaire_pppoe (show_secrets, &password, &service, &mtu, &mac); - - if (!check_and_convert_mtu (mtu, &mtu_int, error)) - goto cleanup_pppoe; - if (!check_mac (mac, ARPHRD_ETHER, "mac", error)) - goto cleanup_pppoe; - - /* Add 'pppoe' setting */ - s_pppoe = (NMSettingPppoe *) nm_setting_pppoe_new (); - nm_connection_add_setting (connection, NM_SETTING (s_pppoe)); - g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, username, NULL); - g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, password, NULL); - g_object_set (s_pppoe, NM_SETTING_PPPOE_SERVICE, service, NULL); - - /* Add ethernet setting */ - s_wired = (NMSettingWired *) nm_setting_wired_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - if (mtu) - g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL); - if (mac) - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); - - success = TRUE; -cleanup_pppoe: - g_free (username_ask); - g_free (password); - g_free (service); - g_free (mtu); - g_free (mac); - if (!success) - return FALSE; - - } else if ( !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME) - || !strcmp (con_type, NM_SETTING_CDMA_SETTING_NAME)) { - /* Build up the settings required for 'gsm' or 'cdma' mobile broadband */ - gboolean success = FALSE; - const char *apn = NULL; - char *apn_ask = NULL; - const char *user_c = NULL; - char *user = NULL; - const char *password_c = NULL; - char *password = NULL; - gboolean is_gsm; - int i = 0; - nmc_arg_t gsm_args[] = { {NULL}, {NULL}, {NULL}, /* placeholders */ - {NULL} }; - - is_gsm = !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME); - - if (is_gsm) - gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !ask}; - gsm_args[i++] = (nmc_arg_t) {"user", TRUE, &user_c, FALSE}; - gsm_args[i++] = (nmc_arg_t) {"password", TRUE, &password_c, FALSE}; - gsm_args[i++] = (nmc_arg_t) {NULL}; - - if (!nmc_parse_args (gsm_args, FALSE, &argc, &argv, error)) - return FALSE; - - if (!apn && ask && is_gsm) - apn = apn_ask = nmc_readline (_("APN: ")); - if (!apn && is_gsm) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'apn' is required.")); - goto cleanup_mobile; - } - - /* Also ask for all optional arguments if '--ask' is specified. */ - user = g_strdup (user_c); - password = g_strdup (password_c); - if (ask) - do_questionnaire_mobile (show_secrets, &user, &password); - - if (is_gsm) { - g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, NULL); - - /* Add 'gsm' setting */ - s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); - nm_connection_add_setting (connection, NM_SETTING (s_gsm)); - g_object_set (s_gsm, - NM_SETTING_GSM_NUMBER, "*99#", - NM_SETTING_GSM_APN, apn, - NM_SETTING_GSM_USERNAME, user, - NM_SETTING_GSM_PASSWORD, password, - NULL); - g_free (apn_ask); - } else { - g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME, NULL); - - /* Add 'cdma' setting */ - s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); - nm_connection_add_setting (connection, NM_SETTING (s_cdma)); - g_object_set (s_cdma, - NM_SETTING_CDMA_NUMBER, "#777", - NM_SETTING_CDMA_USERNAME, user, - NM_SETTING_CDMA_PASSWORD, password, - NULL); - } - - success = TRUE; -cleanup_mobile: - g_free (user); - g_free (password); - if (!success) - return FALSE; - - } else if (!strcmp (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) { - /* Build up the settings required for 'bluetooth' */ - gboolean success = FALSE; - const char *addr = NULL; - char *addr_ask = NULL; - const char *bt_type_c = NULL; - char *bt_type = NULL; - nmc_arg_t exp_args[] = { {"addr", TRUE, &addr, !ask}, - {"bt-type", TRUE, &bt_type_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; - - if (!addr && ask) - addr = addr_ask = nmc_readline (_("Bluetooth device address: ")); - if (!addr) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'addr' is required.")); - return FALSE; - } - if (!check_mac (addr, ARPHRD_ETHER, "addr", error)) - goto cleanup_bt; - - /* Also ask for all optional arguments if '--ask' is specified. */ - bt_type = g_strdup (bt_type_c); - if (ask) - do_questionnaire_bluetooth (&bt_type); - - /* Default to 'panu' if bt-type is not provided. */ - if (!bt_type) - bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_PANU); - - /* Add 'bluetooth' setting */ - s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new (); - nm_connection_add_setting (connection, NM_SETTING (s_bt)); - - if (addr) - g_object_set (s_bt, NM_SETTING_BLUETOOTH_BDADDR, addr, NULL); - - /* 'dun' type requires adding 'gsm' or 'cdma' setting */ - if ( !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN) - || !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) { - bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN); - s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); - nm_connection_add_setting (connection, NM_SETTING (s_gsm)); - g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL); -// g_object_set (s_gsm, NM_SETTING_GSM_APN, "FIXME", NULL; - - } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) { - bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN); - s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); - nm_connection_add_setting (connection, NM_SETTING (s_cdma)); - g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL); - - } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) { - /* no op */ - } else { + if (value) { + if (!nm_utils_iface_valid_name (value) && strcmp (value, "*") != 0) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."), - bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN, - NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma"); - goto cleanup_bt; - } - g_object_set (s_bt, NM_SETTING_BLUETOOTH_TYPE, bt_type, NULL); - - success = TRUE; -cleanup_bt: - g_free (addr_ask); - g_free (bt_type); - if (!success) - return FALSE; - - } else if (!strcmp (con_type, NM_SETTING_VLAN_SETTING_NAME)) { - /* Build up the settings required for 'vlan' */ - gboolean success = FALSE; - const char *parent = NULL; - char *parent_ask = NULL; - const char *vlan_id = NULL; - char *vlan_id_ask = NULL; - unsigned long id = 0; - const char *flags_c = NULL; - char *flags = NULL; - guint32 flags_int = 0; - const char *ingress_c = NULL, *egress_c = NULL; - char *ingress = NULL, *egress = NULL; - char **ingress_arr = NULL, **egress_arr = NULL, **p; - const char *mtu_c = NULL; - char *mtu = NULL; - guint32 mtu_int; - gboolean valid_mac = FALSE; - nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask}, - {"id", TRUE, &vlan_id, !ask}, - {"flags", TRUE, &flags_c, FALSE}, - {"ingress", TRUE, &ingress_c, FALSE}, - {"egress", TRUE, &egress_c, FALSE}, - {"mtu", TRUE, &mtu_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; - - if (!parent && ask) - parent = parent_ask = nmc_readline (_("VLAN parent device or connection UUID: ")); - if (!parent) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'dev' is required.")); + _("Error: '%s' is not a valid interface nor '*'."), + value); return FALSE; } - if (!vlan_id && ask) - vlan_id = vlan_id_ask = nmc_readline (_("VLAN ID <0-4094>: ")); - if (!vlan_id) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'id' is required.")); - goto cleanup_vlan; - } - if (vlan_id) { - if (!nmc_string_to_uint (vlan_id, TRUE, 0, 4094, &id)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'id': '%s' is not valid; use <0-4094>."), - vlan_id); - goto cleanup_vlan; - } - } - - if ( !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN)) - && !nm_utils_is_uuid (parent) - && !nm_utils_iface_valid_name (parent)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."), - parent); - goto cleanup_vlan; - } - - /* Also ask for all optional arguments if '--ask' is specified. */ - mtu = g_strdup (mtu_c); - flags = g_strdup (flags_c); - ingress = g_strdup (ingress_c); - egress = g_strdup (egress_c); - if (ask) - do_questionnaire_vlan (&mtu, &flags, &ingress, &egress); - - if (!check_and_convert_mtu (mtu, &mtu_int, error)) - goto cleanup_vlan; - if (!check_and_convert_vlan_flags (flags, &flags_int, error)) - goto cleanup_vlan; - if (!check_and_convert_vlan_prio_maps (ingress, NM_VLAN_INGRESS_MAP, &ingress_arr, error)) - goto cleanup_vlan; - if (!check_and_convert_vlan_prio_maps (egress, NM_VLAN_EGRESS_MAP, &egress_arr, error)) - goto cleanup_vlan; - - /* Add 'vlan' setting */ - s_vlan = (NMSettingVlan *) nm_setting_vlan_new (); - nm_connection_add_setting (connection, NM_SETTING (s_vlan)); - - /* Add 'wired' setting if necessary */ - if (mtu || valid_mac) { - s_wired = (NMSettingWired *) nm_setting_wired_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - - if (mtu) - g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL); - if (valid_mac) - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL); - } - - /* Set 'vlan' properties */ - if (!valid_mac) - g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL); - - g_object_set (s_vlan, NM_SETTING_VLAN_ID, id, NULL); - - if (flags) - g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, flags_int, NULL); - for (p = ingress_arr; p && *p; p++) - nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, *p); - for (p = egress_arr; p && *p; p++) - nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, *p); - - success = TRUE; -cleanup_vlan: - g_free (mtu); - g_free (flags); - g_free (ingress); - g_free (egress); - g_free (parent_ask); - g_free (vlan_id_ask); - g_strfreev (ingress_arr); - g_strfreev (egress_arr); - if (!success) - return FALSE; - - } else if (!strcmp (con_type, NM_SETTING_BOND_SETTING_NAME)) { - /* Build up the settings required for 'bond' */ - gboolean success = FALSE; - const char *ifname = NULL; - const char *bond_mode_c = NULL; - char *bond_mode = NULL; - const char *bond_primary_c = NULL; - char *bond_primary = NULL; - const char *bond_miimon_c = NULL; - char *bond_miimon = NULL; - const char *bond_downdelay_c = NULL; - char *bond_downdelay = NULL; - const char *bond_updelay_c = NULL; - char *bond_updelay = NULL; - const char *bond_arpinterval_c = NULL; - char *bond_arpinterval = NULL; - const char *bond_arpiptarget_c = NULL; - char *bond_arpiptarget = NULL; - const char *bond_lacp_rate_c = NULL; - char *bond_lacp_rate = NULL; - nmc_arg_t exp_args[] = { {"mode", TRUE, &bond_mode_c, FALSE}, - {"primary", TRUE, &bond_primary_c, FALSE}, - {"miimon", TRUE, &bond_miimon_c, FALSE}, - {"downdelay", TRUE, &bond_downdelay_c, FALSE}, - {"updelay", TRUE, &bond_updelay_c, FALSE}, - {"arp-interval", TRUE, &bond_arpinterval_c, FALSE}, - {"arp-ip-target", TRUE, &bond_arpiptarget_c, FALSE}, - {"lacp-rate", TRUE, &bond_lacp_rate_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; - - /* Also ask for all optional arguments if '--ask' is specified. */ - bond_mode = g_strdup (bond_mode_c); - bond_primary = g_strdup (bond_primary_c); - bond_miimon = g_strdup (bond_miimon_c); - bond_downdelay = g_strdup (bond_downdelay_c); - bond_updelay = g_strdup (bond_updelay_c); - bond_arpinterval = g_strdup (bond_arpinterval_c); - bond_arpiptarget = g_strdup (bond_arpiptarget_c); - bond_lacp_rate = g_strdup (bond_lacp_rate_c); - if (ask) - do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon, - &bond_downdelay, &bond_updelay, - &bond_arpinterval, &bond_arpiptarget, - &bond_lacp_rate); - - /* Generate ifname if connection doesn't have one */ - ifname = nm_setting_connection_get_interface_name (s_con); - if (!ifname) { - char *bond_ifname = unique_master_iface_ifname (all_connections, "nm-bond"); - - g_object_set (s_con, - NM_SETTING_CONNECTION_INTERFACE_NAME, bond_ifname, - NULL); - g_free (bond_ifname); - } - - /* Add 'bond' setting */ - s_bond = (NMSettingBond *) nm_setting_bond_new (); - nm_connection_add_setting (connection, NM_SETTING (s_bond)); - - /* Set bond options */ - if (bond_mode) { - GError *err = NULL; - const char *bm; - if (!(bm = nmc_bond_validate_mode (bond_mode, &err))) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'mode': %s."), err->message); - g_clear_error (&err); - goto cleanup_bond; - } - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, bm); - } - if (bond_primary) { - if (!nm_utils_iface_valid_name (bond_primary)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'primary': '%s' is not a valid interface name."), - bond_primary); - goto cleanup_bond; - } - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, bond_primary); - } - if (bond_miimon) - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, bond_miimon); - if (bond_downdelay && strcmp (bond_downdelay, "0") != 0) - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, bond_downdelay); - if (bond_updelay && strcmp (bond_updelay, "0") != 0) - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, bond_updelay); - if (bond_arpinterval && strcmp (bond_arpinterval, "0") != 0) - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, bond_arpinterval); - if (bond_arpiptarget) - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, bond_arpiptarget); - if (bond_lacp_rate) - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_LACP_RATE, bond_lacp_rate); - - success = TRUE; -cleanup_bond: - g_free (bond_mode); - g_free (bond_primary); - g_free (bond_miimon); - g_free (bond_downdelay); - g_free (bond_updelay); - g_free (bond_arpinterval); - g_free (bond_arpiptarget); - g_free (bond_lacp_rate); - if (!success) - return FALSE; - - } else if (!strcmp (con_type, "bond-slave")) { - /* Slave types without any specific settings ('bond-slave') */ - const char *master = NULL; - const char *type = NULL; - nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE}, - {"type", TRUE, &type, FALSE}, - {NULL} }; - - /* Set global variables for use in TAB completion */ - nmc_tab_completion.con_type = NM_SETTING_BOND_SETTING_NAME; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; - - if (!complete_slave (s_con, all_connections, NM_SETTING_BOND_SETTING_NAME, master, type, ask, error)) - return FALSE; - - /* Change properties in 'connection' setting */ - g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NULL); - - /* Add ethernet setting */ - s_wired = (NMSettingWired *) nm_setting_wired_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - - } else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) { - /* Build up the settings required for 'team' */ - gboolean success = FALSE; - const char *ifname = NULL; - const char *config_c = NULL; - char *config = NULL; - char *json = NULL; - nmc_arg_t exp_args[] = { {"config", TRUE, &config_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; - - /* Also ask for all optional arguments if '--ask' is specified. */ - config = g_strdup (config_c); - if (ask) - do_questionnaire_team (&config); - - /* Generate ifname if conneciton doesn't have one */ - ifname = nm_setting_connection_get_interface_name (s_con); - if (!ifname) { - char *team_ifname = unique_master_iface_ifname (all_connections, "nm-team"); - - g_object_set (s_con, - NM_SETTING_CONNECTION_INTERFACE_NAME, team_ifname, - NULL); - g_free (team_ifname); - } - - /* Add 'team' setting */ - s_team = (NMSettingTeam *) nm_setting_team_new (); - nm_connection_add_setting (connection, NM_SETTING (s_team)); - - if (!nmc_team_check_config (config, &json, error)) { - g_prefix_error (error, _("Error: ")); - goto cleanup_team; - } - - /* Set team options */ - g_object_set (s_team, NM_SETTING_TEAM_CONFIG, json, NULL); - - success = TRUE; -cleanup_team: - g_free (config); - g_free (json); - if (!success) - return FALSE; - - } else if (!strcmp (con_type, "team-slave")) { - /* Build up the settings required for 'team-slave' */ - gboolean success = FALSE; - const char *master = NULL; - char *master_ask = NULL; - const char *type = NULL; - const char *config_c = NULL; - char *config = NULL; - char *json = NULL; - nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE}, - {"type", TRUE, &type, FALSE}, - {"config", TRUE, &config_c, FALSE}, - {NULL} }; - - /* Set global variables for use in TAB completion */ - nmc_tab_completion.con_type = NM_SETTING_TEAM_SETTING_NAME; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; + /* Special value of '*' means no specific interface name */ + if (strcmp (value, "*") == 0) + value = NULL; + } - if (!complete_slave (s_con, all_connections, NM_SETTING_TEAM_SETTING_NAME, master, type, ask, error)) - return FALSE; + return set_property (con, option->setting_name, option->property, value, '\0', error); +} - /* Also ask for all optional arguments if '--ask' is specified. */ - config = g_strdup (config_c); - if (ask) - do_questionnaire_team_slave (&config); +static gboolean +set_connection_master (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error) +{ + NMSettingConnection *s_con; + const char *slave_type; - /* Add 'team-port' setting */ - s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new (); - nm_connection_add_setting (connection, NM_SETTING (s_team_port)); + s_con = nm_connection_get_setting_connection (con); + g_return_val_if_fail (s_con, FALSE); - if (!nmc_team_check_config (config, &json, error)) { - g_prefix_error (error, _("Error: ")); - goto cleanup_team_slave; - } + if (!value) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: master is required")); + return FALSE; + } - /* Set team-port options */ - g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, json, NULL); + slave_type = nm_setting_connection_get_slave_type (s_con); + value = normalized_master_for_slave (nmc->connections, value, slave_type, &slave_type); - success = TRUE; -cleanup_team_slave: - g_free (master_ask); - g_free (config); - g_free (json); - if (!success) - return FALSE; + if (!set_property (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type, + '\0', error)) { + return FALSE; + } - /* Change properties in 'connection' setting */ - g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NULL); - - /* Add ethernet setting */ - s_wired = (NMSettingWired *) nm_setting_wired_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - - } else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) { - /* Build up the settings required for 'bridge' */ - gboolean success = FALSE; - const char *ifname = NULL; - const char *stp_c = NULL; - char *stp = NULL; - const char *priority_c = NULL; - char *priority = NULL; - const char *fwd_delay_c = NULL; - char *fwd_delay = NULL; - const char *hello_time_c = NULL; - char *hello_time = NULL; - const char *max_age_c = NULL; - char *max_age = NULL; - const char *ageing_time_c = NULL; - char *ageing_time = NULL; - const char *mcast_snoop_c = NULL; - char *mcast_snoop = NULL; - gboolean stp_bool, mcast_snoop_bool; - unsigned long stp_prio_int, fwd_delay_int, hello_time_int, - max_age_int, ageing_time_int; - const char *mac_c = NULL; - char *mac = NULL; - nmc_arg_t exp_args[] = { {"stp", TRUE, &stp_c, FALSE}, - {"priority", TRUE, &priority_c, FALSE}, - {"forward-delay", TRUE, &fwd_delay_c, FALSE}, - {"hello-time", TRUE, &hello_time_c, FALSE}, - {"max-age", TRUE, &max_age_c, FALSE}, - {"ageing-time", TRUE, &ageing_time_c, FALSE}, - {"multicast-snooping", TRUE, &mcast_snoop_c, FALSE}, - {"mac", TRUE, &mac_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; + return set_property (con, option->setting_name, option->property, value, '\0', error); +} - /* Also ask for all optional arguments if '--ask' is specified. */ - stp = g_strdup (stp_c); - priority = g_strdup (priority_c); - fwd_delay = g_strdup (fwd_delay_c); - hello_time = g_strdup (hello_time_c); - max_age = g_strdup (max_age_c); - ageing_time = g_strdup (ageing_time_c); - mcast_snoop = g_strdup (mcast_snoop_c); - mac = g_strdup (mac_c); - if (ask) - do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time, - &max_age, &ageing_time, &mcast_snoop, &mac); - - /* Generate ifname if conneciton doesn't have one */ - ifname = nm_setting_connection_get_interface_name (s_con); - if (!ifname) { - char *bridge_ifname = unique_master_iface_ifname (all_connections, "nm-bridge"); - - g_object_set (s_con, - NM_SETTING_CONNECTION_INTERFACE_NAME, bridge_ifname, - NULL); - g_free (bridge_ifname); - } +static gboolean +set_bond_option (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error) +{ + NMSettingBond *s_bond; + gboolean success; - if (stp) { - GError *tmp_err = NULL; - if (!nmc_string_to_bool (stp, &stp_bool, &tmp_err)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'stp': %s."), tmp_err->message); - g_clear_error (&tmp_err); - goto cleanup_bridge; - } - } - if (mcast_snoop) { - GError *tmp_err = NULL; - if (!nmc_string_to_bool (mcast_snoop, &mcast_snoop_bool, &tmp_err)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'multicast-snooping': %s."), tmp_err->message); - g_clear_error (&tmp_err); - goto cleanup_bridge; - } - } + s_bond = nm_connection_get_setting_bond (con); + g_return_val_if_fail (s_bond, FALSE); - /* Add 'bond' setting */ - /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */ - s_bridge = (NMSettingBridge *) nm_setting_bridge_new (); - nm_connection_add_setting (connection, NM_SETTING (s_bridge)); - - if (priority) - if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE, - NM_SETTING_BRIDGE_PRIORITY, &stp_prio_int, error)) - goto cleanup_bridge; - if (fwd_delay) - if (!bridge_prop_string_to_uint (fwd_delay, "forward-delay", NM_TYPE_SETTING_BRIDGE, - NM_SETTING_BRIDGE_FORWARD_DELAY, &fwd_delay_int, error)) - goto cleanup_bridge; - if (hello_time) - if (!bridge_prop_string_to_uint (hello_time, "hello-time", NM_TYPE_SETTING_BRIDGE, - NM_SETTING_BRIDGE_HELLO_TIME, &hello_time_int, error)) - goto cleanup_bridge; - if (max_age) - if (!bridge_prop_string_to_uint (max_age, "max-age", NM_TYPE_SETTING_BRIDGE, - NM_SETTING_BRIDGE_MAX_AGE, &max_age_int, error)) - goto cleanup_bridge; - if (ageing_time) - if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE, - NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error)) - goto cleanup_bridge; - if (!check_mac (mac, ARPHRD_ETHER, "mac", error)) - goto cleanup_bridge; - - /* Set bridge options */ - if (stp) - g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, stp_bool, NULL); - if (priority) - g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, stp_prio_int, NULL); - if (fwd_delay) - g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, fwd_delay_int, NULL); - if (hello_time) - g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, hello_time_int, NULL); - if (max_age) - g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL); - if (ageing_time) - g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL); - if (mcast_snoop) - g_object_set (s_bridge, NM_SETTING_BRIDGE_MULTICAST_SNOOPING, mcast_snoop_bool, NULL); - if (mac) - g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, NULL); - - success = TRUE; -cleanup_bridge: - g_free (stp); - g_free (priority); - g_free (fwd_delay); - g_free (hello_time); - g_free (max_age); - g_free (ageing_time); - g_free (mcast_snoop); - g_free (mac); - if (!success) - return FALSE; + if (!value) + return TRUE; - } else if (!strcmp (con_type, "bridge-slave")) { - /* Build up the settings required for 'bridge-slave' */ - gboolean success = FALSE; - const char *master = NULL; - char *master_ask = NULL; - const char *type = NULL; - const char *priority_c = NULL; - char *priority = NULL; - const char *path_cost_c = NULL; - char *path_cost = NULL; - const char *hairpin_c = NULL; - char *hairpin = NULL; - unsigned long prio_int, path_cost_int; - gboolean hairpin_bool; - nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE}, - {"type", TRUE, &type, FALSE}, - {"priority", TRUE, &priority_c, FALSE}, - {"path-cost", TRUE, &path_cost_c, FALSE}, - {"hairpin", TRUE, &hairpin_c, FALSE}, - {NULL} }; - - /* Set global variables for use in TAB completion */ - nmc_tab_completion.con_type = NM_SETTING_BRIDGE_SETTING_NAME; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + if (strcmp (option->option, "mode") == 0) { + value = nmc_bond_validate_mode (value, error); + if (!value) return FALSE; - if (!complete_slave (s_con, all_connections, NM_SETTING_BRIDGE_SETTING_NAME, master, type, ask, error)) - return FALSE; + if (g_strcmp0 (value, "active-backup") == 0) { + const gchar *primary[] = { "primary", NULL }; + enable_options (NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, primary); + } + + success = nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, value); + } else if (strcmp (option->option, "primary") == 0) + success = nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, value); + else if (strcmp (option->option, "miimon") == 0) + success = nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, value); + else if (strcmp (option->option, "downdelay") == 0) + success = nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, value); + else if (strcmp (option->option, "updelay") == 0) + success = nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, value); + else if (strcmp (option->option, "arp-interval") == 0) + success = nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, value); + else if (strcmp (option->option, "arp-ip-target") == 0) + success = nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, value); + else if (strcmp (option->option, "lacp-rate") == 0) + success = nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_LACP_RATE, value); + else + g_return_val_if_reached (FALSE); - /* Add 'bridge-port' setting */ - /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */ - s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new (); - nm_connection_add_setting (connection, NM_SETTING (s_bridge_port)); - - /* Also ask for all optional arguments if '--ask' is specified. */ - priority = g_strdup (priority_c); - path_cost = g_strdup (path_cost_c); - hairpin = g_strdup (hairpin_c); - if (ask) - do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin); - - if (priority) - if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT, - NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error)) - goto cleanup_bridge_slave; - if (path_cost) - if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT, - NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error)) - goto cleanup_bridge_slave; - if (hairpin) { - GError *tmp_err = NULL; - if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'hairpin': %s."), tmp_err->message); - g_clear_error (&tmp_err); - goto cleanup_bridge_slave; - } - } + if (!success) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: error adding bond option '%s=%s'."), + option->option, value); + } - if (priority) - g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL); - if (path_cost) - g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL); - if (hairpin) - g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL); - - success = TRUE; -cleanup_bridge_slave: - g_free (master_ask); - g_free (priority); - g_free (path_cost); - g_free (hairpin); - if (!success) - return FALSE; + return success; +} - /* Change properties in 'connection' setting */ - g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NULL); - - /* Add ethernet setting */ - s_wired = (NMSettingWired *) nm_setting_wired_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - - } else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) { - /* Build up the settings required for 'vpn' */ - gboolean success = FALSE; - const char *vpn_type = NULL; - char *vpn_type_ask = NULL; - const char *user_c = NULL; - char *user = NULL; - const char *st; - gs_free char *service_type_free = NULL; - const char *service_type = NULL; - nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask}, - {"user", TRUE, &user_c, FALSE}, - {NULL} }; - gs_free const char **plugin_names = NULL; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; +static gboolean +set_bond_monitoring_mode (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error) +{ + NMSettingBond *s_bond; + gs_free gchar *monitor_mode = NULL; + const gchar *miimon_opts[] = { "miimon", "downdelay", "updelay", NULL }; + const gchar *arp_opts[] = { "arp-interval", "arp-ip-target", NULL }; - if (!vpn_type && ask) - vpn_type = vpn_type_ask = nmc_readline (PROMPT_VPN_TYPE); - if (!vpn_type) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'vpn-type' is required.")); - goto cleanup_vpn; - } - if (vpn_type_ask) - vpn_type = g_strstrip (vpn_type_ask); + s_bond = nm_connection_get_setting_bond (con); + g_return_val_if_fail (s_bond, FALSE); - plugin_names = nm_vpn_get_plugin_names (FALSE); - if (!(st = nmc_string_is_valid (vpn_type, plugin_names, NULL))) { - g_print (_("Warning: 'vpn-type': %s not known.\n"), vpn_type); - st = vpn_type; - } + if (value) { + monitor_mode = g_strdup (value); + g_strstrip (monitor_mode); + } else { + monitor_mode = g_strdup (WORD_MIIMON); + } - service_type = nm_vpn_get_service_for_name (st); - if (!service_type) - service_type = service_type_free = nm_vpn_get_service_for_name_default (st); + if (matches (monitor_mode, WORD_MIIMON) == 0) + enable_options (NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, miimon_opts); + else if (matches (monitor_mode, WORD_ARP) == 0) + enable_options (NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, arp_opts); + else { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s' is not a valid monitoring mode; use '%s' or '%s'.\n"), + monitor_mode, WORD_MIIMON, WORD_ARP); + return FALSE; + } - /* Also ask for all optional arguments if '--ask' is specified. */ - user = g_strdup (user_c); - if (ask) - do_questionnaire_vpn (&user); + return TRUE; +} - /* Add 'vpn' setting */ - s_vpn = (NMSettingVpn *) nm_setting_vpn_new (); - nm_connection_add_setting (connection, NM_SETTING (s_vpn)); +static gboolean +set_bluetooth_type (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error) +{ + NMSetting *setting; - g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL); - g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL); + if (!value) + return TRUE; - success = TRUE; -cleanup_vpn: - g_free (vpn_type_ask); - g_free (user); - if (!success) - return FALSE; + /* 'dun' type requires adding 'gsm' or 'cdma' setting */ + if ( !strcmp (value, NM_SETTING_BLUETOOTH_TYPE_DUN) + || !strcmp (value, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) { + value = NM_SETTING_BLUETOOTH_TYPE_DUN; + setting = nm_setting_gsm_new (); + nmc_setting_custom_init (setting); + nm_connection_add_setting (con, setting); + } else if (!strcmp (value, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) { + value = NM_SETTING_BLUETOOTH_TYPE_DUN; + setting = nm_setting_cdma_new (); + nm_connection_add_setting (con, setting); + } else if (!strcmp (value, NM_SETTING_BLUETOOTH_TYPE_PANU)) { + /* no op */ + } else { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."), + value, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN, + NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma"); + return FALSE; + } - } else if (!strcmp (con_type, NM_SETTING_OLPC_MESH_SETTING_NAME)) { - /* Build up the settings required for 'olpc' */ - gboolean success = FALSE; - char *ssid_ask = NULL; - const char *ssid = NULL; - GBytes *ssid_bytes; - const char *channel_c = NULL; - char *channel = NULL; - unsigned long chan; - const char *dhcp_anycast_c = NULL; - char *dhcp_anycast = NULL; - nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask}, - {"channel", TRUE, &channel_c, FALSE}, - {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; + return set_property (con, option->setting_name, option->property, value, '\0', error); +} - if (!ssid && ask) - ssid = ssid_ask = nmc_readline (_("SSID: ")); - if (!ssid) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'ssid' is required.")); - goto cleanup_olpc; - } +static gboolean +set_yes_no (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error) +{ + if (g_strcmp0 (value, _(WORD_LOC_YES))) + value = WORD_YES; + if (g_strcmp0 (value, _(WORD_LOC_NO))) + value = WORD_NO; - /* Also ask for all optional arguments if '--ask' is specified. */ - channel = g_strdup (channel_c); - dhcp_anycast = g_strdup (dhcp_anycast_c); - if (ask) - do_questionnaire_olpc (&channel, &dhcp_anycast); + return set_property (con, option->setting_name, option->property, value, '\0', error); +} - if (channel) { - if (!nmc_string_to_uint (channel, TRUE, 1, 13, &chan)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'channel': '%s' is not valid; use <1-13>."), - channel); - goto cleanup_olpc; - } - } - if (!check_mac (dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", error)) - goto cleanup_olpc; +static gboolean +set_ip4_address (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error) +{ + NMSettingIPConfig *s_ip4; - /* Add OLPC mesh setting */ - s_olpc_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new (); - nm_connection_add_setting (connection, NM_SETTING (s_olpc_mesh)); + if (!value) + return TRUE; - ssid_bytes = g_bytes_new (ssid, strlen (ssid)); - g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_SSID, ssid_bytes, NULL); - if (channel) - g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, chan, NULL); - else - g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, 1, NULL); - if (dhcp_anycast) - g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, dhcp_anycast, NULL); - g_bytes_unref (ssid_bytes); - - success = TRUE; -cleanup_olpc: - g_free (ssid_ask); - g_free (channel); - g_free (dhcp_anycast); - if (!success) - return FALSE; + s_ip4 = nm_connection_get_setting_ip4_config (con); + if (!s_ip4) { + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + nm_connection_add_setting (con, NM_SETTING (s_ip4)); + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NULL); + } + return set_property (con, option->setting_name, option->property, value, '\0', error); +} - } else if (!strcmp (con_type, NM_SETTING_ADSL_SETTING_NAME)) { - /* Build up the settings required for 'adsl' */ - gboolean success = FALSE; - char *username_ask = NULL; - const char *username = NULL; - char *protocol_ask = NULL, *protocol = NULL; - const char *protocol_c = NULL; - const char *password_c = NULL; - char *password = NULL; - const char *encapsulation_c = NULL; - char *encapsulation = NULL; - nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask}, - {"protocol", TRUE, &protocol_c, !ask}, - {"password", TRUE, &password_c, FALSE}, - {"encapsulation", TRUE, &encapsulation_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; +static gboolean +set_ip6_address (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error) +{ + NMSettingIPConfig *s_ip6; - if (!username && ask) - username = username_ask = nmc_readline (_("Username: ")); - if (!username) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'username' is required.")); - goto cleanup_adsl; - } + if (!value) + return TRUE; -#define PROMPT_ADSL_PROTO "(" NM_SETTING_ADSL_PROTOCOL_PPPOA "/" NM_SETTING_ADSL_PROTOCOL_PPPOE "/" NM_SETTING_ADSL_PROTOCOL_IPOATM "): " - if (!protocol_c && ask) - protocol_c = protocol_ask = nmc_readline (_("Protocol %s"), PROMPT_ADSL_PROTO); - if (!protocol_c) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'protocol' is required.")); - goto cleanup_adsl; - } - protocol = g_strdup (protocol_c); - if (!check_adsl_protocol (&protocol, error)) - goto cleanup_adsl; - - /* Also ask for all optional arguments if '--ask' is specified. */ - password = g_strdup (password_c); - encapsulation = g_strdup (encapsulation_c); - if (ask) - do_questionnaire_adsl (show_secrets, &password, &encapsulation); - - if (!check_adsl_encapsulation (&encapsulation, error)) - goto cleanup_adsl; - - /* Add ADSL setting */ - s_adsl = (NMSettingAdsl *) nm_setting_adsl_new (); - nm_connection_add_setting (connection, NM_SETTING (s_adsl)); - - g_object_set (s_adsl, - NM_SETTING_ADSL_USERNAME, username, - NM_SETTING_ADSL_PROTOCOL, protocol, - NM_SETTING_ADSL_PASSWORD, password, - NM_SETTING_ADSL_ENCAPSULATION, encapsulation, + s_ip6 = nm_connection_get_setting_ip6_config (con); + if (!s_ip6) { + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + nm_connection_add_setting (con, NM_SETTING (s_ip6)); + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, NULL); + } + return set_property (con, option->setting_name, option->property, value, '\0', error); +} - success = TRUE; -cleanup_adsl: - g_free (username_ask); - g_free (password); - g_free (protocol); - g_free (protocol_ask); - g_free (encapsulation); - if (!success) - return FALSE; +/*----------------------------------------------------------------------------*/ - } else if (!strcmp (con_type, NM_SETTING_MACVLAN_SETTING_NAME)) { - /* Build up the settings required for 'macvlan' */ - gboolean success = FALSE; - const char *parent = NULL; - char *parent_ask = NULL; - const char *mode = NULL; - char *mode_ask = NULL; - const char *tap_c = NULL; - char *tap = NULL; - NMSettingMacvlanMode mode_enum; - gboolean valid_mac = FALSE; - gboolean tap_bool = FALSE; - nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask}, - {"mode", TRUE, &mode, !ask}, - {"tap", TRUE, &tap_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; +static OptionInfo option_info[] = { + { NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE, "type", OPTION_REQD, PROMPT_CON_TYPE, NULL, + set_connection_type, gen_connection_types }, + { NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_ID, "con-name", OPTION_DONT_ASK, NULL, NULL, NULL, NULL }, + { NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_AUTOCONNECT, "autoconnect", OPTION_DONT_ASK, NULL, NULL, NULL, gen_func_bool_values_l10n }, + { NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME, "ifname", OPTION_REQD, N_("Interface name [*]"), NULL, set_connection_iface, NULL }, + { NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER, "master", OPTION_DONT_ASK, PROMPT_MASTER, NULL, + set_connection_master, gen_func_master_ifnames }, + { NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE, "slave-type", OPTION_DONT_ASK, NULL, NULL, NULL, NULL }, + { NM_SETTING_PPPOE_SETTING_NAME, NM_SETTING_PPPOE_USERNAME, "username", OPTION_REQD, N_("PPPoE username"), NULL, NULL, NULL }, + { NM_SETTING_PPPOE_SETTING_NAME, NM_SETTING_PPPOE_PASSWORD, "password", OPTION_NONE, N_("Password [none]"), NULL, NULL, NULL }, + { NM_SETTING_PPPOE_SETTING_NAME, NM_SETTING_PPPOE_SERVICE, "service", OPTION_NONE, N_("Service [none]"), NULL, NULL, NULL }, + { NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MTU, "mtu", OPTION_NONE, N_("MTU [auto]"), NULL, NULL, NULL }, + { NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS, "mac", OPTION_NONE, N_("MAC [none]"), NULL, NULL, NULL }, + { NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "cloned-mac", OPTION_NONE, N_("Cloned MAC [none]"), NULL, NULL, NULL }, + { NM_SETTING_INFINIBAND_SETTING_NAME, NM_SETTING_INFINIBAND_MTU, "mtu", OPTION_NONE, N_("MTU [auto]"), NULL, NULL, NULL }, + { NM_SETTING_INFINIBAND_SETTING_NAME, NM_SETTING_INFINIBAND_MAC_ADDRESS, "mac", OPTION_NONE, N_("MAC [none]"), NULL, NULL, NULL }, + { NM_SETTING_INFINIBAND_SETTING_NAME, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "transport-mode", OPTION_NONE, PROMPT_IB_MODE, PROMPT_IB_MODE_CHOICES, + NULL, gen_func_ib_type }, + { NM_SETTING_INFINIBAND_SETTING_NAME, NM_SETTING_INFINIBAND_PARENT, "parent", OPTION_NONE, N_("Parent interface [none]"), NULL, NULL, NULL }, + { NM_SETTING_INFINIBAND_SETTING_NAME, NM_SETTING_INFINIBAND_P_KEY, "p-key", OPTION_NONE, N_("P_KEY [none]"), NULL, NULL, NULL }, + { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID, "ssid", OPTION_REQD, N_("SSID"), NULL, NULL, NULL }, + { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MODE, "mode", OPTION_NONE, PROMPT_WIFI_MODE, PROMPT_WIFI_MODE_CHOICES, + NULL, gen_func_wifi_mode }, + { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MTU, "mtu", OPTION_NONE, N_("MTU [auto]"), NULL, NULL, NULL }, + { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS, "mac", OPTION_NONE, N_("MAC [none]"), NULL, NULL, NULL }, + { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "cloned-mac", OPTION_NONE, N_("Cloned MAC [none]"), NULL, NULL, NULL }, + { NM_SETTING_WIMAX_SETTING_NAME, NM_SETTING_WIMAX_NETWORK_NAME, "nsp", OPTION_REQD, N_("WiMAX NSP name"), NULL, NULL, NULL }, + { NM_SETTING_WIMAX_SETTING_NAME, NM_SETTING_WIMAX_MAC_ADDRESS, "mac", OPTION_NONE, N_("MAC [none]"), NULL, NULL, NULL }, + { NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_APN, "apn", OPTION_REQD, N_("APN"), NULL, NULL, NULL }, + { NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_USERNAME, "user", OPTION_NONE, N_("Username [none]"), NULL, NULL, NULL }, + { NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_PASSWORD, "password", OPTION_NONE, N_("Password [none]"), NULL, NULL, NULL }, + { NM_SETTING_CDMA_SETTING_NAME, NM_SETTING_CDMA_USERNAME, "user", OPTION_NONE, N_("Username [none]"), NULL, NULL, NULL }, + { NM_SETTING_CDMA_SETTING_NAME, NM_SETTING_CDMA_PASSWORD, "password", OPTION_NONE, N_("Password [none]"), NULL, NULL, NULL }, + { NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_BDADDR, "addr", OPTION_REQD, N_("Bluetooth device address"), NULL, NULL, NULL }, + { NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE, "bt-type", OPTION_NONE, PROMPT_BT_TYPE, PROMPT_BT_TYPE_CHOICES, + set_bluetooth_type, gen_func_bt_type }, + { NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_PARENT, "dev", OPTION_REQD, N_("VLAN parent device or connection UUID"), NULL, + NULL, NULL }, + { NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_ID, "id", OPTION_REQD, N_("VLAN ID (<0-4094>)"), NULL, NULL, NULL }, + { NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_FLAGS, "flags", OPTION_NONE, N_("VLAN flags (<0-7>) [none]"), NULL, NULL, NULL }, + { NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_INGRESS_PRIORITY_MAP, "ingress", OPTION_NONE, N_("Ingress priority maps [none]"), NULL, NULL, NULL }, + { NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_EGRESS_PRIORITY_MAP, "egress", OPTION_NONE, N_("Egress priority maps [none]"), NULL, NULL, NULL }, + { NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, "mode", OPTION_NONE, PROMPT_BOND_MODE, "[balance-rr]", + set_bond_option, gen_func_bond_mode }, + { NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, "primary", OPTION_DONT_ASK, N_("Bonding primary interface [none]"), + NULL, set_bond_option, NULL }, + { NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, NULL, OPTION_NONE, N_("Bonding monitoring mode"), PROMPT_BOND_MON_MODE_CHOICES, + set_bond_monitoring_mode, gen_func_bond_mon_mode }, + { NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, "miimon", OPTION_DONT_ASK, N_("Bonding miimon [100]"), NULL, set_bond_option, NULL }, + { NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, "downdelay", OPTION_DONT_ASK, N_("Bonding downdelay [0]"), NULL, set_bond_option, NULL }, + { NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, "updelay", OPTION_DONT_ASK, N_("Bonding updelay [0]"), NULL, set_bond_option, NULL }, + { NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, "arp-interval", OPTION_DONT_ASK, N_("Bonding arp-interval [0]"), NULL, + set_bond_option, NULL }, + { NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, "arp-ip-target", OPTION_DONT_ASK, N_("Bonding arp-ip-target [none]"), + NULL, set_bond_option, NULL }, + { NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, "lacp-rate", OPTION_DONT_ASK, N_("LACP rate ('slow' or 'fast') [slow]"), NULL, + set_bond_option, NULL }, + { NM_SETTING_TEAM_SETTING_NAME, NM_SETTING_TEAM_CONFIG, "config", OPTION_NONE, N_("Team JSON configuration [none]"), NULL, NULL, NULL }, + { NM_SETTING_TEAM_PORT_SETTING_NAME, NM_SETTING_TEAM_PORT_CONFIG, "config", OPTION_NONE, N_("Team JSON configuration [none]"), NULL, NULL, NULL }, + { NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_STP, "stp", OPTION_NONE, N_("Enable STP [no]"), NULL, + set_yes_no, gen_func_bool_values_l10n }, + { NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_PRIORITY, "priority", OPTION_NONE, N_("STP priority [32768]"), NULL, NULL, NULL }, + { NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_FORWARD_DELAY, "forward-delay", OPTION_NONE, N_("Forward delay [15]"), NULL, NULL, NULL }, + { NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_HELLO_TIME, "hello-time", OPTION_NONE, N_("Hello time [2]"), NULL, NULL, NULL }, + { NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_MAX_AGE, "max-age", OPTION_NONE, N_("Max age [20]"), NULL, NULL, NULL }, + { NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_AGEING_TIME, "ageing-time", OPTION_NONE, N_("MAC address ageing time [300]"), NULL, NULL, NULL }, + { NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_MULTICAST_SNOOPING, "multicast-snooping", OPTION_NONE, N_("Enable IGMP snooping %s [no]"), NULL, + set_yes_no, gen_func_bool_values_l10n }, + { NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_MAC_ADDRESS, "mac", OPTION_NONE, N_("MAC [none]"), NULL, NULL, NULL }, + { NM_SETTING_BRIDGE_PORT_SETTING_NAME, NM_SETTING_BRIDGE_PORT_PRIORITY, "priority", OPTION_NONE, N_("Bridge port priority [32]"), NULL, NULL, NULL }, + { NM_SETTING_BRIDGE_PORT_SETTING_NAME, NM_SETTING_BRIDGE_PORT_PATH_COST, "path-cost", OPTION_NONE, N_("Bridge port STP path cost [100]"), NULL, NULL, NULL }, + { NM_SETTING_BRIDGE_PORT_SETTING_NAME, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, "hairpin", OPTION_NONE, N_("Hairpin [no]"), NULL, + set_yes_no, gen_func_bool_values_l10n }, + { NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_SERVICE_TYPE, "vpn-type", OPTION_REQD, PROMPT_VPN_TYPE, NULL, NULL, gen_func_vpn_types }, + { NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_USER_NAME, "user", OPTION_NONE, N_("Username [none]"), NULL, NULL, NULL }, + { NM_SETTING_OLPC_MESH_SETTING_NAME, NM_SETTING_OLPC_MESH_SSID, "ssid", OPTION_REQD, N_("SSID"), NULL, NULL, NULL }, + { NM_SETTING_OLPC_MESH_SETTING_NAME, NM_SETTING_OLPC_MESH_CHANNEL, "channel", OPTION_NONE, N_("OLPC Mesh channel [1]"), NULL, NULL, NULL }, + { NM_SETTING_OLPC_MESH_SETTING_NAME, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, "dhcp-anycast", OPTION_NONE, N_("DHCP anycast MAC address [none]"), NULL, NULL, NULL }, + { NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_USERNAME, "username", OPTION_REQD, N_("Username"), NULL, NULL, NULL }, + { NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_PROTOCOL, "protocol", OPTION_REQD, PROMPT_ADSL_PROTO, PROMPT_ADSL_PROTO_CHOICES, + NULL, gen_func_adsl_proto }, + { NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_PASSWORD, "password", OPTION_NONE, N_("Password [none]"), NULL, NULL, NULL }, + { NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_ENCAPSULATION, "encapsulation", OPTION_NONE, PROMPT_ADSL_ENCAP, PROMPT_ADSL_ENCAP_CHOICES, + NULL, gen_func_adsl_encap }, + { NM_SETTING_MACVLAN_SETTING_NAME, NM_SETTING_MACVLAN_PARENT, "dev", OPTION_REQD, N_("MACVLAN parent device or connection UUID"), NULL, + NULL, NULL }, + { NM_SETTING_MACVLAN_SETTING_NAME, NM_SETTING_MACVLAN_MODE, "mode", OPTION_REQD, PROMPT_MACVLAN_MODE, NULL, NULL, gen_func_macvlan_mode }, + { NM_SETTING_MACVLAN_SETTING_NAME, NM_SETTING_MACVLAN_TAP, "tap", OPTION_NONE, N_("Tap [no]"), NULL, + set_yes_no, gen_func_bool_values_l10n }, + { NM_SETTING_VXLAN_SETTING_NAME, NM_SETTING_VXLAN_ID, "id", OPTION_REQD, N_("VXLAN ID"), NULL, NULL, NULL }, + { NM_SETTING_VXLAN_SETTING_NAME, NM_SETTING_VXLAN_REMOTE, "remote", OPTION_REQD, N_("Remote"), NULL, NULL, NULL }, + { NM_SETTING_VXLAN_SETTING_NAME, NM_SETTING_VXLAN_PARENT, "dev", OPTION_NONE, N_("Parent device [none]"), NULL, NULL, NULL }, + { NM_SETTING_VXLAN_SETTING_NAME, NM_SETTING_VXLAN_LOCAL, "local", OPTION_NONE, N_("Local address [none]"), NULL, NULL, NULL }, + { NM_SETTING_VXLAN_SETTING_NAME, NM_SETTING_VXLAN_SOURCE_PORT_MIN, "source-port-min", OPTION_NONE, N_("Minimum source port [0]"), NULL, NULL, NULL }, + { NM_SETTING_VXLAN_SETTING_NAME, NM_SETTING_VXLAN_SOURCE_PORT_MAX, "source-port-max", OPTION_NONE, N_("Maximum source port [0]"), NULL, NULL, NULL }, + { NM_SETTING_VXLAN_SETTING_NAME, NM_SETTING_VXLAN_DESTINATION_PORT, "destination-port", OPTION_NONE, N_("Destination port [8472]"), NULL, NULL, NULL }, + { NM_SETTING_TUN_SETTING_NAME, NM_SETTING_TUN_MODE, "mode", OPTION_NONE, PROMPT_TUN_MODE, PROMPT_TUN_MODE_CHOICES, + NULL, gen_func_tun_mode }, + { NM_SETTING_TUN_SETTING_NAME, NM_SETTING_TUN_OWNER, "owner", OPTION_NONE, N_("User ID [none]"), NULL, NULL, NULL }, + { NM_SETTING_TUN_SETTING_NAME, NM_SETTING_TUN_GROUP, "group", OPTION_NONE, N_("Group ID [none]"), NULL, NULL, NULL }, + { NM_SETTING_TUN_SETTING_NAME, NM_SETTING_TUN_PI, "pi", OPTION_NONE, N_("Enable PI [no]"), NULL, + set_yes_no, gen_func_bool_values_l10n }, + { NM_SETTING_TUN_SETTING_NAME, NM_SETTING_TUN_VNET_HDR, "vnet-hdr", OPTION_NONE, N_("Enable VNET header [no]"), NULL, + set_yes_no, gen_func_bool_values_l10n }, + { NM_SETTING_TUN_SETTING_NAME, NM_SETTING_TUN_MULTI_QUEUE, "multi-queue", OPTION_NONE, N_("Enable multi queue [no]"), NULL, + set_yes_no, gen_func_bool_values_l10n }, + { NM_SETTING_IP_TUNNEL_SETTING_NAME, NM_SETTING_IP_TUNNEL_MODE, "mode", OPTION_REQD, PROMPT_IP_TUNNEL_MODE, NULL, NULL, gen_func_ip_tunnel_mode }, + { NM_SETTING_IP_TUNNEL_SETTING_NAME, NM_SETTING_IP_TUNNEL_LOCAL, "local", OPTION_NONE, N_("Local endpoint [none]"), NULL, NULL, NULL }, + { NM_SETTING_IP_TUNNEL_SETTING_NAME, NM_SETTING_IP_TUNNEL_REMOTE, "remote", OPTION_REQD, N_("Remote"), NULL, NULL, NULL }, + { NM_SETTING_IP_TUNNEL_SETTING_NAME, NM_SETTING_IP_TUNNEL_PARENT, "dev", OPTION_NONE, N_("Parent device [none]"), NULL, NULL, NULL }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP_CONFIG_ADDRESSES, "ip4", OPTION_MULTI, N_("IPv4 address (IP[/plen]) [none]"), NULL, + set_ip4_address, NULL }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP_CONFIG_GATEWAY, "gw4", OPTION_NONE, N_("IPv4 gateway [none]"), NULL, NULL, NULL }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP_CONFIG_ADDRESSES, "ip6", OPTION_MULTI, N_("IPv6 address (IP[/plen]) [none]"), NULL, + set_ip6_address, NULL }, + { NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP_CONFIG_GATEWAY, "gw6", OPTION_NONE, N_("IPv6 gateway [none]"), NULL, NULL, NULL }, + { NULL, NULL, NULL, OPTION_NONE, NULL, NULL, NULL, NULL }, +}; - if (!parent && ask) - parent = parent_ask = nmc_readline (_("MACVLAN parent device or connection UUID: ")); - if (!parent) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'dev' is required.")); - return FALSE; - } +static gboolean +option_relevant (NMConnection *connection, OptionInfo *option) +{ + if (option->flags & OPTION_DONT_ASK && !(option->flags & OPTION_ENABLED)) + return FALSE; + if (option->flags & OPTION_DISABLED) + return FALSE; + if (!nm_connection_get_setting_by_name (connection, option->setting_name)) + return FALSE; + return TRUE; +} - if ( !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN)) - && !nm_utils_is_uuid (parent) - && !nm_utils_iface_valid_name (parent)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."), - parent); - goto cleanup_macvlan; - } +/*----------------------------------------------------------------------------*/ - if (!mode && ask) - mode = mode_ask = nmc_readline (PROMPT_MACVLAN_MODE); - if (!mode) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'mode' is required.")); - return FALSE; - } +static void +complete_property_name (NmCli *nmc, NMConnection *connection, + char modifier, + const gchar *prefix, + const gchar *postfix) +{ + NMSettingConnection *s_con; + const NameItem *valid_settings_main = NULL; + const NameItem *valid_settings_slave = NULL; + const char *connection_type = NULL; + const char *slave_type = NULL; + gs_free char *slv_type = NULL; + gs_free char *word_list = NULL; + OptionInfo *candidate; - if (!nm_utils_enum_from_str (nm_setting_macvlan_mode_get_type(), mode, (int *) &mode_enum, NULL)) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'mode' is not valid.")); - return FALSE; - } + connection_type = nm_connection_get_connection_type (connection); + s_con = nm_connection_get_setting_connection (connection); + if (s_con) + slave_type = nm_setting_connection_get_slave_type (s_con); + slv_type = g_strdup_printf ("%s-slave", slave_type ? slave_type : "no"); + valid_settings_main = get_valid_settings_array (connection_type); + valid_settings_slave = get_valid_settings_array (slv_type); - /* Also ask for all optional arguments if '--ask' is specified. */ - tap = g_strdup (tap_c); - if (ask) - do_questionnaire_macvlan (&tap); + word_list = get_valid_properties_string (valid_settings_main, valid_settings_slave, modifier, prefix, postfix); + if (word_list) + g_print ("%s", word_list); - if (tap) { - GError *tmp_err = NULL; - if (!nmc_string_to_bool (tap, &tap_bool, &tmp_err)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'tap': %s."), tmp_err->message); - g_clear_error (&tmp_err); - goto cleanup_macvlan; - } - } + if (modifier != '\0') + return; - /* Add 'macvlan' setting */ - s_macvlan = (NMSettingMacvlan *) nm_setting_macvlan_new (); - nm_connection_add_setting (connection, NM_SETTING (s_macvlan)); + for (candidate = option_info; candidate->setting_name; candidate++) { + if (!nm_connection_get_setting_by_name (connection, candidate->setting_name)) + continue; + if (!candidate->option) + continue; + if (!g_str_has_prefix (candidate->option, prefix)) + continue; + g_print ("%s\n", candidate->option); + } +} - /* Add 'wired' setting if necessary */ - if (valid_mac) { - s_wired = (NMSettingWired *) nm_setting_wired_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL); - } +static void +run_rl_generator (rl_compentry_func_t *generator_func, const char *prefix) +{ + int state = 0; + char *str; - /* Set 'macvlan' properties */ - if (!valid_mac) - g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, parent, NULL); - g_object_set (s_macvlan, NM_SETTING_MACVLAN_MODE, mode_enum, NULL); - g_object_set (s_macvlan, NM_SETTING_MACVLAN_TAP, tap_bool, NULL); + while ((str = generator_func (prefix, state))) { + g_print ("%s\n", str); + g_free (str); + if (state == 0) + state = 1; + } +} - success = TRUE; -cleanup_macvlan: - g_free (parent_ask); - g_free (mode_ask); - g_free (tap); +static void +complete_option (OptionInfo *option, const gchar *prefix) +{ + if (option->generator_func) + run_rl_generator (option->generator_func, prefix); +} - if (!success) - return FALSE; +static void +complete_property (const gchar *setting_name, const gchar *property, const gchar *prefix) +{ + if (strcmp (setting_name, NM_SETTING_CONNECTION_SETTING_NAME) == 0) { + if (strcmp (property, NM_SETTING_CONNECTION_TYPE) == 0) + run_rl_generator (gen_connection_types, prefix); + else if (strcmp (property, NM_SETTING_CONNECTION_MASTER) == 0) + run_rl_generator (gen_func_master_ifnames, prefix); + } else if ( strcmp (setting_name, NM_SETTING_VPN_SETTING_NAME) == 0 + && strcmp (property, NM_SETTING_VPN_SERVICE_TYPE) == 0) + run_rl_generator (gen_func_vpn_types, prefix); + else if ( strcmp (setting_name, NM_SETTING_WIRELESS_SETTING_NAME) == 0 + && strcmp (property, NM_SETTING_WIRELESS_MODE) == 0) + run_rl_generator (gen_func_wifi_mode, prefix); + else if ( strcmp (setting_name, NM_SETTING_INFINIBAND_SETTING_NAME) == 0 + && strcmp (property, NM_SETTING_INFINIBAND_TRANSPORT_MODE) == 0) + run_rl_generator (gen_func_ib_type, prefix); + else if ( strcmp (setting_name, NM_SETTING_BLUETOOTH_SETTING_NAME) == 0 + && strcmp (property, NM_SETTING_BLUETOOTH_TYPE) == 0) + run_rl_generator (gen_func_bt_type, prefix); + else if (strcmp (setting_name, NM_SETTING_ADSL_SETTING_NAME) == 0) { + if (strcmp (property, NM_SETTING_ADSL_PROTOCOL) == 0) + run_rl_generator (gen_func_adsl_proto, prefix); + else if (strcmp (property, NM_SETTING_ADSL_ENCAPSULATION) == 0) + run_rl_generator (gen_func_adsl_encap, prefix); + } else if ( strcmp (setting_name, NM_SETTING_TUN_SETTING_NAME) == 0 + && strcmp (property, NM_SETTING_TUN_MODE) == 0) + run_rl_generator (gen_func_tun_mode, prefix); + else if ( strcmp (setting_name, NM_SETTING_IP_TUNNEL_SETTING_NAME) == 0 + && strcmp (property, NM_SETTING_IP_TUNNEL_MODE) == 0) + run_rl_generator (gen_func_ip_tunnel_mode, prefix); + else if ( strcmp (setting_name, NM_SETTING_MACVLAN_SETTING_NAME) == 0 + && strcmp (property, NM_SETTING_MACVLAN_MODE) == 0) + run_rl_generator (gen_func_macvlan_mode, prefix); - } else if (!strcmp (con_type, NM_SETTING_TUN_SETTING_NAME)) { - /* Build up the settings required for 'tun' */ - gboolean success = FALSE; - const char *mode_c = NULL; - char *mode_ask = NULL, *mode = NULL; - NMSettingTunMode mode_enum; - const char *owner_c = NULL, *group_c = NULL; - char *owner = NULL, *group = NULL; - const char *pi_c = NULL, *vnet_hdr_c = NULL, *multi_queue_c = NULL; - char *pi = NULL, *vnet_hdr = NULL, *multi_queue = NULL; - gboolean pi_bool, vnet_hdr_bool, multi_queue_bool; - nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !ask}, - {"owner", TRUE, &owner_c, FALSE}, - {"group", TRUE, &group_c, FALSE}, - {"pi", TRUE, &pi_c, FALSE}, - {"vnet-hdr", TRUE, &vnet_hdr_c, FALSE}, - {"multi-queue", TRUE, &multi_queue_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; +} - if (!mode_c && ask) { - mode_ask = nmc_readline (_("Mode %s"), PROMPT_TUN_MODE); - mode_ask = mode_ask ? mode_ask : g_strdup ("tun"); - mode_c = mode_ask; - } - if (!mode_c) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'mode' is required.")); - goto cleanup_tun; - } - mode = g_strdup (mode_c); - if (!check_tun_mode (&mode, error)) - goto cleanup_tun; - - if (owner && !check_user_group_id (owner, error)) - goto cleanup_tun; - if (group && !check_user_group_id (group, error)) - goto cleanup_tun; - - owner = g_strdup (owner_c); - group = g_strdup (group_c); - pi = g_strdup (pi_c); - vnet_hdr = g_strdup (vnet_hdr_c); - multi_queue = g_strdup (multi_queue_c); - if (ask) - do_questionnaire_tun (&owner, &group, &pi, &vnet_hdr, &multi_queue); - - if (pi) { - GError *tmp_err = NULL; +/*----------------------------------------------------------------------------*/ - if (!nmc_string_to_bool (pi, &pi_bool, &tmp_err)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'pi': %s."), tmp_err->message); - g_clear_error (&tmp_err); - goto cleanup_tun; - } - } +static gboolean +get_value (const char **value, int *argc, char ***argv, const char *option, GError **error) +{ + if (!**argv) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: value for '%s' is missing."), option); + return FALSE; + } - if (vnet_hdr) { - GError *tmp_err = NULL; + /* Empty string will reset the value to default */ + if (**argv[0] == '\0') + *value = NULL; + else + *value = *argv[0]; - if (!nmc_string_to_bool (vnet_hdr, &vnet_hdr_bool, &tmp_err)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'vnet-hdr': %s."), tmp_err->message); - g_clear_error (&tmp_err); - goto cleanup_tun; - } - } + next_arg (argc, argv); + return TRUE; +} - if (multi_queue) { - GError *tmp_err = NULL; +gboolean +nmc_read_connection_properties (NmCli *nmc, + NMConnection *connection, + int *argc, + char ***argv, + GError **error) +{ + const char *option; + const char *value = NULL; + GError *local = NULL; - if (!nmc_string_to_bool (multi_queue, &multi_queue_bool, &tmp_err)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'multi-queue': %s."), tmp_err->message); - g_clear_error (&tmp_err); - goto cleanup_tun; - } - } - /* Add 'tun' setting */ - s_tun = (NMSettingTun *) nm_setting_tun_new (); - nm_connection_add_setting (connection, NM_SETTING (s_tun)); - mode_enum = !strcmp (mode, "tun") ? NM_SETTING_TUN_MODE_TUN : NM_SETTING_TUN_MODE_TAP; - - g_object_set (s_tun, - NM_SETTING_TUN_MODE, mode_enum, - NM_SETTING_TUN_OWNER, owner, - NM_SETTING_TUN_GROUP, group, - NULL); - if (pi) - g_object_set (s_tun, NM_SETTING_TUN_PI, pi_bool, NULL); - if (vnet_hdr) - g_object_set (s_tun, NM_SETTING_TUN_VNET_HDR, vnet_hdr_bool, NULL); - if (multi_queue) - g_object_set (s_tun, NM_SETTING_TUN_MULTI_QUEUE, multi_queue_bool, NULL); - - success = TRUE; -cleanup_tun: - g_free (mode_ask); - g_free (mode); - g_free (owner); - g_free (group); - g_free (pi); - g_free (vnet_hdr); - g_free (multi_queue); - if (!success) - return FALSE; + /* First check if we have a slave-type, as this would mean we will not + * have ip properties but possibly others, slave-type specific. + */ + /* Go through arguments and set properties */ + do { + OptionInfo *candidate; + OptionInfo *chosen = NULL; + gs_strfreev gchar **strv = NULL; + const NameItem *type_settings, *slv_settings; + char modifier = '\0'; - } else if (!strcmp (con_type, NM_SETTING_IP_TUNNEL_SETTING_NAME)) { - /* Build up the settings required for 'ip-tunnel' */ - const char *mode_c = NULL, *local_c = NULL, *remote_c = NULL; - char *mode_ask = NULL, *remote_ask = NULL, *local = NULL; - const char *parent_c = NULL; - char *parent = NULL; - gboolean success = FALSE; - NMIPTunnelMode mode_enum; - nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !ask}, - {"local", TRUE, &local_c, FALSE}, - {"remote", TRUE, &remote_c, !ask}, - {"dev", TRUE, &parent_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + if (!con_settings (connection, &type_settings, &slv_settings, error)) return FALSE; - if (!mode_c && ask) - mode_c = mode_ask = nmc_readline (PROMPT_IP_TUNNEL_MODE); - if (!mode_c) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'mode' is required.")); - goto cleanup_tunnel; - } - - if (!nm_utils_enum_from_str (nm_ip_tunnel_mode_get_type (), - mode_c, (int *) &mode_enum, NULL)) { - gs_free const char **values = NULL; - gs_free char *values_str = NULL; + ensure_settings (connection, slv_settings); + ensure_settings (connection, type_settings); - values = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (), - NM_IP_TUNNEL_MODE_UNKNOWN + 1, - G_MAXINT); - values_str = g_strjoinv (",", (char **) values); - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'mode': '%s' is not valid, use one of %s"), - mode_c, values_str); - goto cleanup_tunnel; - } - - if (!remote_c && ask) - remote_c = remote_ask = nmc_readline (_("Remote endpoint: ")); - if (!remote_c) { + option = **argv; + if (!option) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'remote' is required.")); - goto cleanup_tunnel; - } - - if ( !nm_utils_ipaddr_valid (AF_INET, remote_c) - && !nm_utils_ipaddr_valid (AF_INET6, remote_c)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'remote': '%s' is not valid; must be an IP address"), - remote_c); - goto cleanup_tunnel; - } - - local = g_strdup (local_c); - parent = g_strdup (parent_c); - if (ask) - do_questionnaire_ip_tunnel (&local, &parent); - - if ( local - && !nm_utils_ipaddr_valid (AF_INET, local) - && !nm_utils_ipaddr_valid (AF_INET6, local)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'local': '%s' is not valid; must be an IP address"), - local); - goto cleanup_tunnel; - } - - if (parent) { - if ( !nm_utils_is_uuid (parent) - && !nm_utils_iface_valid_name (parent)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'dev': '%s' is neither UUID nor interface name."), - parent); - goto cleanup_tunnel; - } - } - - /* Add 'tunnel' setting */ - s_ip_tunnel = (NMSettingIPTunnel *) nm_setting_ip_tunnel_new (); - nm_connection_add_setting (connection, NM_SETTING (s_ip_tunnel)); - - /* Set 'tunnel' properties */ - g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_MODE, mode_enum, NULL); - g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_REMOTE, remote_c, NULL); - if (local) - g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_LOCAL, local, NULL); - if (parent) - g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_PARENT, parent, NULL); - - /* Set default values for IPv6 tunnels */ - if (nm_utils_ipaddr_valid (AF_INET6, remote_c)) { - g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_TOS, 64, NULL); - g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT, 4, NULL); - } - - success = TRUE; -cleanup_tunnel: - g_free (remote_ask); - g_free (mode_ask); - g_free (parent); - g_free (local); - if (!success) - return FALSE; - - } else if (!strcmp (con_type, NM_SETTING_VXLAN_SETTING_NAME)) { - /* Build up the settings required for 'vxlan' */ - gboolean success = FALSE; - char *id_ask = NULL; - const char *id = NULL; - char *remote_ask = NULL; - const char *remote = NULL; - const char *parent_c = NULL, *local_c = NULL; - const char *src_port_min_c = NULL, *src_port_max_c = NULL; - const char *dst_port_c = NULL; - char *parent = NULL, *local = NULL; - char *src_port_min = NULL, *src_port_max = NULL, *dst_port = NULL; - unsigned long int vni; - unsigned long sport_min = G_MAXULONG, sport_max = G_MAXULONG; - unsigned long dport = G_MAXULONG; - nmc_arg_t exp_args[] = { {"id", TRUE, &id, !ask}, - {"remote", TRUE, &remote, !ask}, - {"dev", TRUE, &parent_c, FALSE}, - {"local", TRUE, &local_c, FALSE}, - {"source-port-min", TRUE, &src_port_min_c, FALSE}, - {"source-port-max", TRUE, &src_port_max_c, FALSE}, - {"destination-port", TRUE, &dst_port_c, FALSE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) + _("Error: <setting>.<property> argument is missing.")); return FALSE; - - if (!id && ask) - id = id_ask = nmc_readline (_("VXLAN ID: ")); - if (!id) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'id' is required.")); - goto cleanup_vxlan; - } - - if (!remote && ask) - remote = remote_ask = nmc_readline (_("Remote: ")); - if (!remote) { - g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'remote' is required.")); - goto cleanup_vxlan; - } - - if (!nmc_string_to_uint (id, TRUE, 0, (1UL << 24) - 1, &vni)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'id': '%s' is not valid; use <0-16777215>."), id); - goto cleanup_vxlan; - } - - parent = g_strdup (parent_c); - local = g_strdup (local_c); - src_port_min = g_strdup (src_port_min_c); - src_port_max = g_strdup (src_port_max_c); - dst_port = g_strdup (dst_port_c); - - if (ask) - do_questionnaire_vxlan (&parent, &local, &src_port_min, &src_port_max, &dst_port); - - if (parent) { - if ( !nm_utils_is_uuid (parent) - && !nm_utils_iface_valid_name (parent)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'dev': '%s' is neither UUID nor interface name."), - parent); - goto cleanup_vxlan; - } } - if ( !nm_utils_ipaddr_valid (AF_INET, remote) - && !nm_utils_ipaddr_valid (AF_INET6, remote)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'remote': '%s' is not a valid IP address"), - remote); - goto cleanup_vxlan; - } + if (option[0] == '+' || option[0] == '-') + modifier = *option; - if (local) { - if ( !nm_utils_ipaddr_valid (AF_INET, local) - && !nm_utils_ipaddr_valid (AF_INET6, local)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'local': '%s' is not a valid IP address"), - local); - goto cleanup_vxlan; - } - } + strv = g_strsplit (option, ".", 2); + if (g_strv_length (strv) == 2) { + /* This seems like a <setting>.<property> (such as "connection.id" or "bond.mode"), + * optionally prefixed with "+| or "-". */ + char *setting = strv[0]; + const char *setting_name; - if (src_port_min) { - if (!nmc_string_to_uint (src_port_min, TRUE, 0, 65535, &sport_min)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'source-port-min': %s is not valid; use <0-65535>."), - src_port_min); - goto cleanup_vxlan; - } - } + if (modifier) + setting++; - if (src_port_max) { - if (!nmc_string_to_uint (src_port_max, TRUE, 0, 65535, &sport_max)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'source-port-max': %s is not valid; use <0-65535>."), - src_port_max); - goto cleanup_vxlan; - } - } + if (*argc == 1 && nmc->complete) + complete_property_name (nmc, connection, modifier, setting, strv[1]); - if (dst_port) { - if (!nmc_string_to_uint (dst_port, TRUE, 0, 65535, &dport)) { + setting_name = check_valid_name (setting, type_settings, slv_settings, &local); + if (!setting_name) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'destination-port': %s is not valid; use <0-65535>."), - dst_port); - goto cleanup_vxlan; + _("Error: invalid or not allowed setting '%s': %s."), + setting, local->message); + g_clear_error (&local); + return FALSE; } - } - - /* Add 'vxlan' setting */ - s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new (); - nm_connection_add_setting (connection, NM_SETTING (s_vxlan)); - - g_object_set (s_vxlan, NM_SETTING_VXLAN_ID, (guint) vni, NULL); - g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE, remote, NULL); - g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL, local, NULL); - g_object_set (s_vxlan, NM_SETTING_VXLAN_PARENT, parent, NULL); - - if (sport_min != G_MAXULONG) - g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MIN, sport_min, NULL); - if (sport_max != G_MAXULONG) - g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MAX, sport_max, NULL); - if (dport != G_MAXULONG) - g_object_set (s_vxlan, NM_SETTING_VXLAN_DESTINATION_PORT, dport, NULL); - - success = TRUE; - -cleanup_vxlan: - g_free (id_ask); - g_free (remote_ask); - g_free (parent); - g_free (local); - g_free (src_port_min); - g_free (src_port_max); - if (!success) - return FALSE; - } else if (!strcmp (con_type, NM_SETTING_GENERIC_SETTING_NAME)) { - /* Add 'generic' setting */ - s_generic = (NMSettingGeneric *) nm_setting_generic_new (); - nm_connection_add_setting (connection, NM_SETTING (s_generic)); - } else { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: '%s' is not a valid connection type."), - con_type); - return FALSE; - } - - if (!nm_setting_connection_get_slave_type (s_con)) { - /* Read and add IP configuration */ - NMIPAddress *ip4addr = NULL, *ip6addr = NULL; - const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL; - nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE}, - {"ip6", TRUE, &ip6, FALSE}, {"gw6", TRUE, &gw6, FALSE}, - {NULL} }; - - while (argc) { - nmc_arg_t *p; - - /* reset 'found' flag */ - for (p = exp_args; p->name; p++) - p->found = FALSE; - - ip4 = gw4 = ip6 = gw6 = NULL; - - if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, NULL)) - break; - - /* coverity[dead_error_begin] */ - if (ip4) { - ip4addr = nmc_parse_and_build_address (AF_INET, ip4, error); - if (!ip4addr) { - g_prefix_error (error, _("Error: ")); - return FALSE; - } - add_ip4_address_to_connection (ip4addr, connection); - } + next_arg (argc, argv); + if (!get_value (&value, argc, argv, option, error)) + return FALSE; - /* coverity[dead_error_begin] */ - if (gw4) { - NMSettingIPConfig *s_ip = nm_connection_get_setting_ip4_config (connection); + if (!*argc && nmc->complete) + complete_property (setting, strv[1], value ? value : ""); - if (!s_ip) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: IPv4 gateway specified without IPv4 addresses")); - return FALSE; - } else if (nm_setting_ip_config_get_gateway (s_ip)) { + if (!set_property (connection, setting_name, strv[1], value, modifier, error)) + return FALSE; + } else { + /* Let's see if this is an property alias (such as "id", "mode", "type" or "con-name")*/ + for (candidate = option_info; candidate->setting_name; candidate++) { + if (g_strcmp0 (candidate->option, option)) + continue; + if (!check_valid_name (candidate->setting_name, type_settings, slv_settings, NULL)) + continue; + if (chosen) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: multiple IPv4 gateways specified")); + _("Error: '%s' is ambiguous (%s.%s or %s.%s)."), option, + chosen->setting_name, chosen->property, + candidate->setting_name, candidate->property); return FALSE; - } else if (!nm_utils_ipaddr_valid (AF_INET, gw4)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: Invalid IPv4 gateway '%s'"), - gw4); } - - g_object_set (s_ip, - NM_SETTING_IP_CONFIG_GATEWAY, gw4, - NULL); + chosen = candidate; } - /* coverity[dead_error_begin] */ - if (ip6) { - ip6addr = nmc_parse_and_build_address (AF_INET6, ip6, error); - if (!ip6addr) { - g_prefix_error (error, _("Error: ")); - return FALSE; - } - add_ip6_address_to_connection (ip6addr, connection); + if (!chosen) { + if (modifier) + option++; + if (*argc == 1 && nmc->complete) + complete_property_name (nmc, connection, modifier, option, NULL); + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: invalid <setting>.<property> '%s'."), option); + return FALSE; } - /* coverity[dead_error_begin] */ - if (gw6) { - NMSettingIPConfig *s_ip = nm_connection_get_setting_ip6_config (connection); - - if (!s_ip) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: IPv6 gateway specified without IPv6 addresses")); - return FALSE; - } else if (nm_setting_ip_config_get_gateway (s_ip)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: multiple IPv6 gateways specified")); - return FALSE; - } else if (!nm_utils_ipaddr_valid (AF_INET, gw6)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: Invalid IPv6 gateway '%s'"), - gw6); - } - - g_object_set (s_ip, - NM_SETTING_IP_CONFIG_GATEWAY, gw6, - NULL); - } - } + if (*argc == 1 && nmc->complete) + complete_property_name (nmc, connection, modifier, option, NULL); - /* Ask for addresses if '--ask' is specified. */ - if (ask) - do_questionnaire_ip (connection); - } + next_arg (argc, argv); + if (!get_value (&value, argc, argv, option, error)) + return FALSE; - if (argc) { - /* Set extra connection properties. */ - nmc_arg_t exp_args[] = { {"--", FALSE, NULL, TRUE}, - {NULL} }; + if (!*argc && nmc->complete) + complete_option (chosen, value ? value : ""); - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) - return FALSE; + if (!set_option (nmc, connection, chosen, value, error)) + return FALSE; + } - if (!read_connection_properties (connection, argc, argv, error)) - return FALSE; - } + } while (*argc); return TRUE; } @@ -6697,135 +4582,6 @@ update_connection (gboolean persistent, NULL, callback, user_data); } -static char * -gen_func_vpn_types (const char *text, int state) -{ - gs_free const char **plugin_names = NULL; - - plugin_names = nm_vpn_get_plugin_names (FALSE); - return nmc_rl_gen_func_basic (text, state, plugin_names); -} - -static char * -gen_func_bool_values_l10n (const char *text, int state) -{ - const char *words[] = { WORD_LOC_YES, WORD_LOC_NO, NULL }; - return nmc_rl_gen_func_basic (text, state, words); -} - -static char * -gen_func_wifi_mode (const char *text, int state) -{ - const char *words[] = { "infrastructure", "ap", "adhoc", NULL }; - return nmc_rl_gen_func_basic (text, state, words); -} - -static char * -gen_func_ib_type (const char *text, int state) -{ - const char *words[] = { "datagram", "connected", NULL }; - return nmc_rl_gen_func_basic (text, state, words); -} - -static char * -gen_func_bt_type (const char *text, int state) -{ - const char *words[] = { "panu", "dun-gsm", "dun-cdma", NULL }; - return nmc_rl_gen_func_basic (text, state, words); -} - -static char * -gen_func_bond_mode (const char *text, int state) -{ - const char *words[] = { "balance-rr", "active-backup", "balance-xor", "broadcast", - "802.3ad", "balance-tlb", "balance-alb", NULL }; - return nmc_rl_gen_func_basic (text, state, words); -} -static char * -gen_func_bond_mon_mode (const char *text, int state) -{ - const char *words[] = { "miimon", "arp", NULL }; - return nmc_rl_gen_func_basic (text, state, words); -} - -static char * -gen_func_adsl_proto (const char *text, int state) -{ - const char *words[] = { "pppoe", "pppoa", "ipoatm", NULL }; - return nmc_rl_gen_func_basic (text, state, words); -} - -static char * -gen_func_adsl_encap (const char *text, int state) -{ - const char *words[] = { "vcmux", "llc", NULL }; - return nmc_rl_gen_func_basic (text, state, words); -} - -static char * -gen_func_tun_mode (const char *text, int state) -{ - const char *words[] = { "tun", "tap", NULL }; - return nmc_rl_gen_func_basic (text, state, words); -} - -static char * -gen_func_ip_tunnel_mode (const char *text, int state) -{ - gs_free const char **words = NULL; - - words = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (), - NM_IP_TUNNEL_MODE_UNKNOWN + 1, - G_MAXINT); - return nmc_rl_gen_func_basic (text, state, words); -} - -static char * -gen_func_macvlan_mode (const char *text, int state) -{ - gs_free const char **words = NULL; - - words = nm_utils_enum_get_values (nm_setting_macvlan_mode_get_type(), - NM_SETTING_MACVLAN_MODE_UNKNOWN + 1, - G_MAXINT); - return nmc_rl_gen_func_basic (text, state, words); -} - -static char * -gen_func_master_ifnames (const char *text, int state) -{ - int i; - GPtrArray *ifnames; - char *ret; - NMConnection *con; - NMSettingConnection *s_con; - const char *con_type, *ifname; - - if (!nm_cli.connections) - return NULL; - - /* Disable appending space after completion */ - rl_completion_append_character = '\0'; - - ifnames = g_ptr_array_sized_new (20); - for (i = 0; i < nm_cli.connections->len; i++) { - con = NM_CONNECTION (nm_cli.connections->pdata[i]); - s_con = nm_connection_get_setting_connection (con); - g_assert (s_con); - con_type = nm_setting_connection_get_connection_type (s_con); - if (g_strcmp0 (con_type, nmc_tab_completion.con_type) != 0) - continue; - ifname = nm_connection_get_interface_name (con); - g_ptr_array_add (ifnames, (gpointer) ifname); - } - g_ptr_array_add (ifnames, (gpointer) NULL); - - ret = nmc_rl_gen_func_basic (text, state, (const char **) ifnames->pdata); - - g_ptr_array_free (ifnames, TRUE); - return ret; -} - static gboolean is_single_word (const char* line) { @@ -6846,47 +4602,47 @@ nmcli_con_add_tab_completion (const char *text, int start, int end) { char **match_array = NULL; rl_compentry_func_t *generator_func = NULL; + gs_free char *no = g_strdup_printf (": [%s]", gettext ("no")); + gs_free char *yes = g_strdup_printf (": [%s]", gettext ("yes")); /* Disable readline's default filename completion */ rl_attempted_completion_over = 1; /* Restore standard append character to space */ - rl_completion_append_character = ' '; + rl_completion_append_character = '\x00'; if (!is_single_word (rl_line_buffer)) return NULL; - if (g_strcmp0 (rl_prompt, PROMPT_CON_TYPE) == 0) + if (g_str_has_prefix (rl_prompt, PROMPT_CON_TYPE)) generator_func = gen_connection_types; - else if (g_strcmp0 (rl_prompt, PROMPT_VPN_TYPE) == 0) + else if (g_str_has_prefix (rl_prompt, PROMPT_VPN_TYPE)) generator_func = gen_func_vpn_types; - else if (g_strcmp0 (rl_prompt, PROMPT_MASTER) == 0) + else if (g_str_has_prefix (rl_prompt, PROMPT_MASTER)) generator_func = gen_func_master_ifnames; - else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL)) - || g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, ":")) - || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL)) - || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, ":"))) - generator_func = gen_func_bool_values_l10n; - else if (g_str_has_suffix (rl_prompt, PROMPT_WIFI_MODE)) + else if (g_str_has_prefix (rl_prompt, PROMPT_WIFI_MODE)) generator_func = gen_func_wifi_mode; - else if (g_str_has_suffix (rl_prompt, PROMPT_IB_MODE)) + else if (g_str_has_prefix (rl_prompt, PROMPT_IB_MODE)) generator_func = gen_func_ib_type; - else if (g_str_has_suffix (rl_prompt, PROMPT_BT_TYPE)) + else if (g_str_has_prefix (rl_prompt, PROMPT_BT_TYPE)) generator_func = gen_func_bt_type; else if (g_str_has_prefix (rl_prompt, PROMPT_BOND_MODE)) generator_func = gen_func_bond_mode; - else if (g_str_has_suffix (rl_prompt, PROMPT_BOND_MON_MODE)) + else if (g_str_has_prefix (rl_prompt, PROMPT_BOND_MON_MODE)) generator_func = gen_func_bond_mon_mode; - else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_PROTO)) + else if (g_str_has_prefix (rl_prompt, PROMPT_ADSL_PROTO)) generator_func = gen_func_adsl_proto; - else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_ENCAP)) + else if (g_str_has_prefix (rl_prompt, PROMPT_ADSL_ENCAP)) generator_func = gen_func_adsl_encap; - else if (g_str_has_suffix (rl_prompt, PROMPT_TUN_MODE)) + else if (g_str_has_prefix (rl_prompt, PROMPT_TUN_MODE)) generator_func = gen_func_tun_mode; - else if (g_str_has_suffix (rl_prompt, PROMPT_IP_TUNNEL_MODE)) + else if (g_str_has_prefix (rl_prompt, PROMPT_IP_TUNNEL_MODE)) generator_func = gen_func_ip_tunnel_mode; - else if (g_str_has_suffix (rl_prompt, PROMPT_MACVLAN_MODE)) + else if (g_str_has_prefix (rl_prompt, PROMPT_MACVLAN_MODE)) generator_func = gen_func_macvlan_mode; + else if ( g_str_has_suffix (rl_prompt, yes) + || g_str_has_suffix (rl_prompt, no)) + generator_func = gen_func_bool_values_l10n; if (generator_func) match_array = rl_completion_matches (text, generator_func); @@ -6894,171 +4650,281 @@ nmcli_con_add_tab_completion (const char *text, int start, int end) return match_array; } -static NMCResultCode -do_connection_add (NmCli *nmc, int argc, char **argv) +static void +ask_option (NmCli *nmc, NMConnection *connection, OptionInfo *option) { - NMConnection *connection = NULL; - NMSettingConnection *s_con; - char *uuid; - char *default_name = NULL; - const char *type = NULL; - char *type_ask = NULL; - const char *con_name = NULL; - const char *autoconnect = NULL; - gboolean auto_bool = TRUE; - const char *ifname = NULL; - char *ifname_ask = NULL; - gboolean ifname_mandatory = TRUE; - const char *save = NULL; - gboolean save_bool = TRUE; - const char *master = NULL; - const char *checked_master = NULL; - const char *slave_type = NULL; - AddConnectionInfo *info = NULL; - const char *setting_name; + gchar *value; GError *error = NULL; - nmc_arg_t exp_args[] = { {"type", TRUE, &type, !nmc->ask}, - {"con-name", TRUE, &con_name, FALSE}, - {"autoconnect", TRUE, &autoconnect, FALSE}, - {"ifname", TRUE, &ifname, FALSE}, - {"save", TRUE, &save, FALSE}, - {"master", TRUE, &master, FALSE}, - {"slave-type", TRUE, &slave_type, FALSE}, - {NULL} }; - - rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion; - - nmc->return_value = NMC_RESULT_SUCCESS; + gs_free gchar *prompt = NULL; + gboolean multi = option->flags & OPTION_MULTI; + + prompt = g_strjoin ("", + gettext (option->prompt), + option->def_hint ? " " : "", + option->def_hint ? option->def_hint : "", + ": ", + NULL); + + if (multi) + g_print (_("You can specify this option more than once. Press <Enter> when you're done.\n")); + +again: + value = nmc_readline ("%s", prompt); + if (multi && !value) + return; - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) { - g_string_assign (nmc->return_text, error->message); - nmc->return_value = error->code; + if (!set_option (nmc, connection, option, value, &error)) { + g_printerr ("%s\n", error->message); g_clear_error (&error); - goto error; + goto again; } - if (!type && nmc->ask) { - char *types_tmp = get_valid_options_string (nmc_valid_connection_types, NULL); - g_print ("Valid types: [%s]\n", types_tmp); - type = type_ask = nmc_readline (PROMPT_CON_TYPE); - g_free (types_tmp); - } - if (!type) { - g_string_printf (nmc->return_text, _("Error: 'type' argument is required.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - if (type_ask) - type = g_strstrip (type_ask); + if (multi && value) + goto again; +} - if (!(setting_name = check_valid_name (type, nmc_valid_connection_types, NULL, &error))) { - g_string_printf (nmc->return_text, _("Error: invalid connection type; %s."), - error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - g_clear_error (&error); - goto error; - } - if (autoconnect) { - GError *tmp_err = NULL; - if (!nmc_string_to_bool (autoconnect, &auto_bool, &tmp_err)) { - g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."), - tmp_err->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - g_clear_error (&tmp_err); - goto error; - } - } - if (save) { - GError *tmp_err = NULL; - if (!nmc_string_to_bool (save, &save_bool, &tmp_err)) { - g_string_printf (nmc->return_text, _("Error: 'save': %s."), - tmp_err->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - g_clear_error (&tmp_err); - goto error; - } +static void +questionnaire_mandatory (NmCli *nmc, NMConnection *connection) +{ + OptionInfo *candidate; + + /* Mandatory settings. */ + for (candidate = option_info; candidate->setting_name; candidate++) { + if (!option_relevant (connection, candidate)) + continue; + if (candidate->flags & OPTION_REQD || candidate->flags & OPTION_ENABLED) + ask_option (nmc, connection, candidate); } +} - /* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */ - if ( strcmp (type, NM_SETTING_BOND_SETTING_NAME) == 0 - || strcmp (type, NM_SETTING_TEAM_SETTING_NAME) == 0 - || strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME) == 0 - || strcmp (type, NM_SETTING_VLAN_SETTING_NAME) == 0) - ifname_mandatory = FALSE; +static gboolean +want_provide_opt_args (const char *type, int num) +{ + char *answer; + gboolean ret = TRUE; - if (!ifname && ifname_mandatory) { - if (nmc->ask) { - ifname = ifname_ask = nmc_readline (_("Interface name [*]: ")); - if (!ifname) - ifname = ifname_ask = g_strdup ("*"); - } else { - if (!*argv) - g_string_printf (nmc->return_text, _("Error: 'ifname' argument is required.")); - else - g_string_printf (nmc->return_text, _("Error: mandatory 'ifname' not seen before '%s'."), - *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } + /* Ask for optional arguments. */ + g_print (ngettext ("There is %d optional setting for %s.\n", + "There are %d optional settings for %s.\n", num), + num, type); + answer = nmc_readline (ngettext ("Do you want to provide it? %s", + "Do you want to provide them? %s", num), + prompt_yes_no (TRUE, NULL)); + answer = answer ? g_strstrip (answer) : NULL; + if (answer && matches (answer, WORD_LOC_YES) != 0) + ret = FALSE; + g_free (answer); + return ret; +} + +static const char * +setting_name_to_name (const char *name) +{ + if (strcmp (name, NM_SETTING_WIRED_SETTING_NAME) == 0) + return _("Wired Ethernet"); + if (strcmp (name, NM_SETTING_INFINIBAND_SETTING_NAME) == 0) + return _("InfiniBand connection"); + if (strcmp (name, NM_SETTING_WIRELESS_SETTING_NAME) == 0) + return _("Wi-Fi connection"); + if (strcmp (name, NM_SETTING_WIMAX_SETTING_NAME) == 0) + return _("WiMAX connection"); + if (strcmp (name, NM_SETTING_PPPOE_SETTING_NAME) == 0) + return _("PPPoE"); + if (strcmp (name, NM_SETTING_CDMA_SETTING_NAME) == 0) + return _("CDMA mobile broadband connection"); + if (strcmp (name, NM_SETTING_GSM_SETTING_NAME) == 0) + return _("GSM mobile broadband connection"); + if (strcmp (name, NM_SETTING_BLUETOOTH_SETTING_NAME) == 0) + return _("bluetooth connection"); + if (strcmp (name, NM_SETTING_VLAN_SETTING_NAME) == 0) + return _("VLAN connection"); + if (strcmp (name, NM_SETTING_BOND_SETTING_NAME) == 0) + return _("Bond device"); + if (strcmp (name, NM_SETTING_TEAM_SETTING_NAME) == 0) + return _("Team device"); + if (strcmp (name, NM_SETTING_TEAM_PORT_SETTING_NAME) == 0) + return _("Team port"); + if (strcmp (name, NM_SETTING_BRIDGE_SETTING_NAME) == 0) + return _("Bridge device"); + if (strcmp (name, NM_SETTING_BRIDGE_PORT_SETTING_NAME) == 0) + return _("Bridge port"); + if (strcmp (name, NM_SETTING_VPN_SETTING_NAME) == 0) + return _("VPN connection"); + if (strcmp (name, NM_SETTING_OLPC_MESH_SETTING_NAME) == 0) + return _("OLPC Mesh connection"); + if (strcmp (name, NM_SETTING_ADSL_SETTING_NAME) == 0) + return _("ADSL connection"); + if (strcmp (name, NM_SETTING_MACVLAN_SETTING_NAME) == 0) + return _("macvlan connection"); + if (strcmp (name, NM_SETTING_VXLAN_SETTING_NAME) == 0) + return _("VXLAN connection"); + if (strcmp (name, NM_SETTING_TUN_SETTING_NAME) == 0) + return _("Tun device"); + if (strcmp (name, NM_SETTING_IP4_CONFIG_SETTING_NAME) == 0) + return _("IPv4 protocol"); + if (strcmp (name, NM_SETTING_IP6_CONFIG_SETTING_NAME) == 0) + return _("IPv6 protocol"); + + /* Should not happen; but let's still try to be somewhat sensible. */ + return name; +} + +static gboolean +questionnaire_one_optional (NmCli *nmc, NMConnection *connection) +{ + OptionInfo *candidate; + + /* Optional settings. */ + const gchar *setting_name = NULL; + int count = 0; + + /* Find first setting with relevant options and count them. */ + for (candidate = option_info; candidate->setting_name; candidate++) { + if (!option_relevant (connection, candidate)) + continue; + if (!setting_name) + setting_name = candidate->setting_name; + else if (strcmp (setting_name, candidate->setting_name)) + break; + count++; } - if (ifname) { - if (!nm_utils_iface_valid_name (ifname) && strcmp (ifname, "*") != 0) { - g_string_printf (nmc->return_text, - _("Error: 'ifname': '%s' is not a valid interface nor '*'."), - ifname); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + if (!setting_name) + return FALSE; + + /* Now ask for the settings. */ + if (want_provide_opt_args (setting_name_to_name (setting_name), count)) { + for (candidate = option_info; candidate->setting_name; candidate++) { + if (!option_relevant (connection, candidate)) + continue; + if (strcmp (setting_name, candidate->setting_name)) + continue; + ask_option (nmc, connection, candidate); } - /* Special value of '*' means no specific interface name */ - if (strcmp (ifname, "*") == 0) - ifname = NULL; } + /* Make sure we won't ask again. */ + disable_options (setting_name, NULL); + + return TRUE; +} + +static NMCResultCode +do_connection_add (NmCli *nmc, int argc, char **argv) +{ + NMConnection *connection = NULL; + NMSettingConnection *s_con; + GError *error = NULL; + AddConnectionInfo *info = NULL; + gboolean save_bool = TRUE; + OptionInfo *candidate; + gboolean seen_dash_dash = FALSE; + + rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion; + + nmc->return_value = NMC_RESULT_SUCCESS; + /* Create a new connection object */ connection = nm_simple_connection_new (); /* Build up the 'connection' setting */ s_con = (NMSettingConnection *) nm_setting_connection_new (); - uuid = nm_utils_uuid_generate (); - if (con_name) - default_name = g_strdup (con_name); - else { - char *try_name = ifname ? - g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname) - : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types)); - default_name = nmc_unique_connection_name (nmc->connections, try_name); - g_free (try_name); - } - - if (master) - /* Verify master argument */ - checked_master = normalized_master_for_slave (nmc->connections, master, slave_type, &slave_type); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, default_name, - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_TYPE, setting_name, - NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool, - NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, - NM_SETTING_CONNECTION_MASTER, checked_master, - NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type, - NULL); - g_free (uuid); - g_free (default_name); nm_connection_add_setting (connection, NM_SETTING (s_con)); - if (!complete_connection_by_type (connection, - setting_name, - nmc->connections, - nmc->ask, - nmc->show_secrets, - argc, - argv, - &error)) { +read_properties: + /* Get the arguments from the command line if any */ + if (argc && !nmc_read_connection_properties (nmc, connection, &argc, &argv, &error)) { + if (g_strcmp0 (*argv, "--") == 0 && !seen_dash_dash) { + /* This is for compatibility with older nmcli that required + * options and properties to be separated with "--" */ + g_clear_error (&error); + seen_dash_dash = TRUE; + next_arg (&argc, &argv); + goto read_properties; + } else if (g_strcmp0 (*argv, "save") == 0) { + /* It would be better if "save" was a separate argument and not + * mixed with properties, but there's not much we can do about it now. */ + g_clear_error (&error); + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, + _("Error: value for '%s' argument is required."), + "save"); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } + if (!nmc_string_to_bool (*argv, &save_bool, &error)) { + g_string_printf (nmc->return_text, _("Error: 'save': %s."), + error->message); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + g_clear_error (&error); + goto finish; + } + next_arg (&argc, &argv); + goto read_properties; + } + g_string_assign (nmc->return_text, error->message); nmc->return_value = error->code; g_clear_error (&error); - goto error; + goto finish; + } + + if (nmc->complete) + goto finish; + + /* Now ask user for the rest of the mandatory options. */ + if (nmc->ask) + questionnaire_mandatory (nmc, connection); + + /* Traditionally, we didn't ask for these options for ethernet slaves. They don't + * make much sense, since these are likely to be set by the master anyway. */ + if (nm_setting_connection_get_slave_type (s_con)) { + disable_options (NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MTU); + disable_options (NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS); + disable_options (NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_CLONED_MAC_ADDRESS); + } + + /* Connection id is special in that it's required but we don't insist + * on getting it from the user -- we just make up something sensible. */ + if (!nm_setting_connection_get_id (s_con)) { + const char *ifname = nm_setting_connection_get_interface_name (s_con); + const char *type = nm_setting_connection_get_connection_type (s_con); + const char *slave_type = nm_setting_connection_get_slave_type (s_con); + char *try_name, *default_name; + + /* If only bother when there's a type, which is not guaranteed at this point. + * Otherwise the validation will fail anyway. */ + if (type) { + try_name = ifname ? g_strdup_printf ("%s-%s", get_name_alias (type, slave_type, nmc_valid_connection_types), ifname) + : g_strdup (get_name_alias (type, slave_type, nmc_valid_connection_types)); + default_name = nmc_unique_connection_name (nmc->connections, try_name); + g_free (try_name); + g_object_set (s_con, NM_SETTING_CONNECTION_ID, default_name, NULL); + g_free (default_name); + } + } + + /* For some software connection types we generate the interface name for the user. */ + set_default_interface_name (nmc, s_con); + + /* Now see if there's something optional that needs to be asked for. + * Keep asking until there's no more things to ask for. */ + do { + /* This ensures all settings that make sense are present. */ + nm_connection_normalize (connection, NULL, NULL, NULL); + } while (nmc->ask && questionnaire_one_optional (nmc, connection)); + + /* Mandatory settings. No good reason to check this other than guarding the user + * from doing something that's not likely to make sense (such as missing ifname + * on a bond/bridge/team, etc.). Added just to preserve traditional behavior, it + * perhaps is a good idea to just remove this. */ + for (candidate = option_info; candidate->setting_name; candidate++) { + if (!option_relevant (connection, candidate)) + continue; + if (candidate->flags & OPTION_REQD) { + g_string_printf (nmc->return_text, _("Error: '%s' argument is required."), candidate->option); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } } nmc->should_wait++; @@ -7074,21 +4940,14 @@ do_connection_add (NmCli *nmc, int argc, char **argv) add_connection_cb, info); +finish: + reset_options (); if (connection) g_object_unref (connection); return nmc->return_value; - -error: - if (connection) - g_object_unref (connection); - g_free (type_ask); - g_free (ifname_ask); - - return nmc->return_value; } - /*----------------------------------------------------------------------------*/ /* Functions for readline TAB completion in editor */ @@ -7100,7 +4959,7 @@ uuid_display_hook (char **array, int len, int max_len) char *tmp; const char *id; for (i = 1; i <= len; i++) { - con = nmc_find_connection (nmc_tab_completion.nmc->connections, "uuid", array[i], NULL); + con = nmc_find_connection (nmc_tab_completion.nmc->connections, "uuid", array[i], NULL, FALSE); id = con ? nm_connection_get_id (con) : NULL; if (id) { tmp = g_strdup_printf ("%s (%s)", array[i], id); @@ -9006,7 +6865,6 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t gboolean cmd_loop = TRUE; char *cmd_arg = NULL; char *cmd_arg_s, *cmd_arg_p, *cmd_arg_v; - const char *BASE_PROMPT = "nmcli> "; const NameItem *valid_settings_main = NULL; const NameItem *valid_settings_slave = NULL; char *valid_settings_str = NULL; @@ -9187,6 +7045,14 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t break; } nmc_setting_custom_init (setting); + + if (NM_IS_SETTING_WIRELESS (setting)) + nmc_setting_wireless_connect_handlers (NM_SETTING_WIRELESS (setting)); + else if (NM_IS_SETTING_IP4_CONFIG (setting)) + nmc_setting_ip4_connect_handlers (NM_SETTING_IP_CONFIG (setting)); + else if (NM_IS_SETTING_IP6_CONFIG (setting)) + nmc_setting_ip6_connect_handlers (NM_SETTING_IP_CONFIG (setting)); + nm_connection_add_setting (connection, setting); } /* Set global variable for use in TAB completion */ @@ -9814,21 +7680,10 @@ editor_init_new_connection (NmCli *nmc, NMConnection *connection) base_setting = nmc_setting_new_for_name (con_type); if (!base_setting) return; + nmc_setting_custom_init (base_setting); nm_connection_add_setting (connection, base_setting); - /* Set a sensible bond/team/bridge interface name by default */ - if (g_strcmp0 (con_type, NM_SETTING_BOND_SETTING_NAME) == 0) - g_object_set (s_con, - NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bond", - NULL); - if (g_strcmp0 (con_type, NM_SETTING_TEAM_SETTING_NAME) == 0) - g_object_set (s_con, - NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-team", - NULL); - if (g_strcmp0 (con_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0) - g_object_set (s_con, - NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bridge", - NULL); + set_default_interface_name (nmc, s_con); /* Set sensible initial VLAN values */ if (g_strcmp0 (con_type, NM_SETTING_VLAN_SETTING_NAME) == 0) { @@ -9836,52 +7691,9 @@ editor_init_new_connection (NmCli *nmc, NMConnection *connection) g_object_set (NM_SETTING_VLAN (base_setting), NM_SETTING_VLAN_PARENT, dev_ifname ? dev_ifname : "eth0", - NM_SETTING_VLAN_ID, 1, - NULL); - g_object_set (s_con, - NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0", - NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_VLAN_SETTING_NAME, - NULL); - } - - /* Initialize 'transport-mode' so that 'infiniband' is valid */ - if (g_strcmp0 (con_type, NM_SETTING_INFINIBAND_SETTING_NAME) == 0) - g_object_set (NM_SETTING_INFINIBAND (base_setting), - NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", - NULL); - - /* Initialize 'number' so that 'cdma' is valid */ - if (g_strcmp0 (con_type, NM_SETTING_CDMA_SETTING_NAME) == 0) - g_object_set (NM_SETTING_CDMA (base_setting), - NM_SETTING_CDMA_NUMBER, "#777", - NULL); - - /* Initialize 'number' so that 'gsm' is valid */ - if (g_strcmp0 (con_type, NM_SETTING_GSM_SETTING_NAME) == 0) - g_object_set (NM_SETTING_GSM (base_setting), - NM_SETTING_GSM_NUMBER, "*99#", - NULL); - - /* Wi-Fi */ - if (g_strcmp0 (con_type, NM_SETTING_WIRELESS_SETTING_NAME) == 0) { - /* For Wi-Fi set mode to "infrastructure". Even though mode == NULL - * is regarded as "infrastructure", explicit value makes no doubts. - */ - g_object_set (NM_SETTING_WIRELESS (base_setting), - NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, NULL); - - /* Do custom initialization for wifi setting */ - nmc_setting_custom_init (base_setting); } - /* ADSL */ - if (g_strcmp0 (con_type, NM_SETTING_ADSL_SETTING_NAME) == 0) { - /* Initialize a protocol */ - g_object_set (NM_SETTING_ADSL (base_setting), - NM_SETTING_ADSL_PROTOCOL, NM_SETTING_ADSL_PROTOCOL_PPPOE, - NULL); - } /* Always add IPv4 and IPv6 settings for non-slave connections */ setting = nm_setting_ip4_config_new (); @@ -9917,7 +7729,7 @@ editor_init_existing_connection (NMConnection *connection) } static NMCResultCode -do_connection_edit (NmCli *nmc, int argc, char **argv) +do_connection_edit_func (NmCli *nmc, int argc, char **argv) { NMConnection *connection = NULL; NMSettingConnection *s_con; @@ -9942,6 +7754,10 @@ do_connection_edit (NmCli *nmc, int argc, char **argv) {"path", TRUE, &con_path, FALSE}, {NULL} }; + /* TODO: complete uuid, path or id */ + if (nmc->complete) + return nmc->return_value; + nmc->return_value = NMC_RESULT_SUCCESS; if (argc == 1) @@ -9985,7 +7801,7 @@ do_connection_edit (NmCli *nmc, int argc, char **argv) /* Existing connection */ NMConnection *found_con; - found_con = nmc_find_connection (nmc->connections, selector, con, NULL); + found_con = nmc_find_connection (nmc->connections, selector, con, NULL, FALSE); if (!found_con) { g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; @@ -10044,7 +7860,7 @@ do_connection_edit (NmCli *nmc, int argc, char **argv) default_name = g_strdup (con_name); else default_name = nmc_unique_connection_name (nmc->connections, - get_name_alias (connection_type, nmc_valid_connection_types)); + get_name_alias (connection_type, NULL, nmc_valid_connection_types)); g_object_set (s_con, NM_SETTING_CONNECTION_ID, default_name, @@ -10098,6 +7914,46 @@ error: return nmc->return_value; } +typedef struct { + NmCli *nmc; + int argc; + char **argv; +} NmcEditorThreadData; + +static GThread *editor_thread; +static NmcEditorThreadData editor_thread_data; + +/* + * We need to run do_connection_edit_func() in a thread so that + * glib main loop is not blocked and could receive and process D-Bus + * return messages. + */ +static gpointer +connection_editor_thread_func (gpointer data) +{ + NmcEditorThreadData *td = (NmcEditorThreadData *) data; + + /* run editor for editing/adding connections */ + td->nmc->return_value = do_connection_edit_func (td->nmc, td->argc, td->argv); + + /* quit glib main loop now that we are done with this thread */ + quit (); + + return NULL; +} + +static NMCResultCode +do_connection_edit (NmCli *nmc, int argc, char **argv) +{ + nmc->should_wait++; + editor_thread_data.nmc = nmc; + editor_thread_data.argc = argc; + editor_thread_data.argv = argv; + editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data); + g_thread_unref (editor_thread); + + return nmc->return_value; +} static void modify_connection_cb (GObject *connection, @@ -10126,72 +7982,50 @@ modify_connection_cb (GObject *connection, static NMCResultCode do_connection_modify (NmCli *nmc, - gboolean temporary, int argc, char **argv) { NMConnection *connection = NULL; NMRemoteConnection *rc = NULL; - const char *name; - const char *selector = NULL; GError *error = NULL; + gboolean temporary = FALSE; - nmc->return_value = NMC_RESULT_SUCCESS; - - if (argc == 0) { - g_string_printf (nmc->return_text, _("Error: No arguments provided.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - if ( strcmp (*argv, "id") == 0 - || strcmp (*argv, "uuid") == 0 - || strcmp (*argv, "path") == 0) { - - selector = *argv; - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), - selector); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + if (argc && nmc_arg_is_option (*argv, "temporary")) { + if (nmc->complete) goto finish; - } - name = *argv; - } - name = *argv; - if (!name) { - g_string_printf (nmc->return_text, _("Error: connection ID is missing.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; + temporary = TRUE; + next_arg (&argc, &argv); } - connection = nmc_find_connection (nmc->connections, selector, name, NULL); + + connection = get_connection (nmc, &argc, &argv, NULL, &error); if (!connection) { - g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = error->code; goto finish; } + rc = nm_client_get_connection_by_uuid (nmc->client, nm_connection_get_uuid (connection)); if (!rc) { - g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name); + g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), + nm_connection_get_uuid (connection)); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto finish; } - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - - if (!read_connection_properties (NM_CONNECTION (rc), argc, argv, &error)) { + if (!nmc_read_connection_properties (nmc, NM_CONNECTION (rc), &argc, &argv, &error)) { g_string_assign (nmc->return_text, error->message); nmc->return_value = error->code; g_clear_error (&error); goto finish; } - update_connection (!temporary, rc, modify_connection_cb, nmc); + if (nmc->complete) + goto finish; + update_connection (!temporary, rc, modify_connection_cb, nmc); nmc->should_wait++; + finish: return nmc->return_value; } @@ -10237,70 +8071,64 @@ clone_connection_cb (GObject *client, } static NMCResultCode -do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv) +do_connection_clone (NmCli *nmc, int argc, char **argv) { NMConnection *connection = NULL; NMConnection *new_connection = NULL; NMSettingConnection *s_con; CloneConnectionInfo *info; - const char *name; const char *new_name; - char *name_ask = NULL; char *new_name_ask = NULL; - const char *selector = NULL; char *uuid; + gboolean temporary = FALSE; + char **arg_arr = NULL; + int arg_num; + char ***argv_ptr = &argv; + int *argc_ptr = &argc; + GError *error = NULL; - if (argc == 0) { - if (nmc->ask) { - name = name_ask = nmc_readline (PROMPT_CONNECTION); - new_name = new_name_ask = nmc_readline (_("New connection name: ")); - } else { - g_string_printf (nmc->return_text, _("Error: No arguments provided.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - } else { - if ( strcmp (*argv, "id") == 0 - || strcmp (*argv, "uuid") == 0 - || strcmp (*argv, "path") == 0) { + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "temporary", NULL); - selector = *argv; - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), - selector); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - } - name = *argv; - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: <new name> argument is missing.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - new_name = *argv; - if (next_arg (&argc, &argv) == 0) { - g_string_printf (nmc->return_text, _("Error: unexpected extra argument '%s'."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } + if (argc == 0 && nmc->ask) { + char *line; + + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + + line = nmc_readline ("%s: ", PROMPT_CONNECTION); + nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); + g_free (line); + argv_ptr = &arg_arr; + argc_ptr = &arg_num; + } else if (nmc_arg_is_option (*argv, "temporary")) { + temporary = TRUE; + next_arg (&argc, &argv); } - if (!name) { - g_string_printf (nmc->return_text, _("Error: connection ID is missing.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, &error); + if (!connection) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = error->code; goto finish; } - if (!new_name) { + + if (nmc->complete) + goto finish; + + if (argv[0]) + new_name = *argv; + else if (nmc->ask) + new_name = new_name_ask = nmc_readline (_("New connection name: ")); + else { g_string_printf (nmc->return_text, _("Error: <new name> argument is missing.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto finish; } - connection = nmc_find_connection (nmc->connections, selector, name, NULL); - if (!connection) { - g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + if (next_arg (argc_ptr, argv_ptr) == 0) { + g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto finish; } @@ -10332,11 +8160,10 @@ do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv) clone_connection_cb, info); - nmc->should_wait = TRUE; + nmc->should_wait++; finish: if (new_connection) g_object_unref (new_connection); - g_free (name_ask); g_free (new_name_ask); return nmc->return_value; @@ -10367,44 +8194,38 @@ do_connection_delete (NmCli *nmc, int argc, char **argv) NMConnection *connection; ConnectionCbInfo *info = NULL; GSList *queue = NULL, *iter; - char **arg_arr = NULL; + char **arg_arr = NULL, *old_arg; char **arg_ptr = argv; int arg_num = argc; GString *invalid_cons = NULL; int pos = 0; + GError *error = NULL; if (nmc->timeout == -1) nmc->timeout = 10; if (argc == 0) { if (nmc->ask) { - char *line = nmc_readline (PROMPT_CONNECTIONS); + char *line; + + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + + line = nmc_readline ("%s: ", PROMPT_CONNECTIONS); nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); g_free (line); arg_ptr = arg_arr; } if (arg_num == 0) { g_string_printf (nmc->return_text, _("Error: No connection specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto finish; } } while (arg_num > 0) { - const char *selector = NULL; - - if ( strcmp (*arg_ptr, "id") == 0 - || strcmp (*arg_ptr, "uuid") == 0 - || strcmp (*arg_ptr, "path") == 0) { - selector = *arg_ptr; - if (next_arg (&arg_num, &arg_ptr) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - } - - connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos); + old_arg = *arg_ptr; + connection = get_connection (nmc, &arg_num, &arg_ptr, &pos, &error); if (connection) { /* Check if the connection is unique. */ /* Calling delete for the same connection repeatedly would result in @@ -10412,23 +8233,28 @@ do_connection_delete (NmCli *nmc, int argc, char **argv) if (!g_slist_find (queue, connection)) queue = g_slist_prepend (queue, g_object_ref (connection)); } else { - g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr); - g_string_printf (nmc->return_text, _("Error: not all active connections found.")); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + if (!nmc->complete) + g_printerr (_("Error: %s.\n"), error->message); + g_string_printf (nmc->return_text, _("Error: not all connections found.")); + nmc->return_value = error->code; + g_clear_error (&error); + + if (nmc->return_value != NMC_RESULT_ERROR_NOT_FOUND) + goto finish; + if (!invalid_cons) invalid_cons = g_string_new (NULL); - g_string_append_printf (invalid_cons, "'%s', ", *arg_ptr); + g_string_append_printf (invalid_cons, "'%s', ", old_arg); } - - /* Take next argument (if there's no other connection of the same name) */ - if (!pos) - next_arg (&arg_num, &arg_ptr); } if (!queue) { - g_string_printf (nmc->return_text, _("Error: no connection provided.")); + g_string_printf (nmc->return_text, _("Error: No connection specified.")); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto finish; + } else if (nmc->complete) { + g_slist_free (queue); + goto finish; } queue = g_slist_reverse (queue); @@ -10505,10 +8331,15 @@ connection_removed (NMClient *client, NMRemoteConnection *con, NmCli *nmc) static NMCResultCode do_connection_monitor (NmCli *nmc, int argc, char **argv) { + GError *error = NULL; + if (argc == 0) { /* No connections specified. Monitor all. */ int i; + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + nmc->connections = nm_client_get_connections (nmc->client); for (i = 0; i < nmc->connections->len; i++) connection_watch (nmc, g_ptr_array_index (nmc->connections, i)); @@ -10519,38 +8350,26 @@ do_connection_monitor (NmCli *nmc, int argc, char **argv) } else { /* Look up the specified connections and watch them. */ NMConnection *connection; - char **arg_ptr = argv; - int arg_num = argc; int pos = 0; do { - const char *selector = NULL; - - if ( strcmp (*arg_ptr, "id") == 0 - || strcmp (*arg_ptr, "uuid") == 0 - || strcmp (*arg_ptr, "path") == 0) { - selector = *arg_ptr; - if (next_arg (&arg_num, &arg_ptr) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); - return NMC_RESULT_ERROR_USER_INPUT; - } - } - - connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos); - if (connection) { - connection_watch (nmc, connection); - } else { - g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr); + connection = get_connection (nmc, &argc, &argv, &pos, &error); + if (!connection) { + if (!nmc->complete) + g_printerr (_("Error: %s.\n"), error->message); g_string_printf (nmc->return_text, _("Error: not all connections found.")); - return NMC_RESULT_ERROR_NOT_FOUND; + return error->code; } - /* Take next argument (if there's no other connection of the same name) */ - if (!pos) - next_arg (&arg_num, &arg_ptr); - } while (arg_num > 0); + if (nmc->complete) + continue; + + connection_watch (nmc, connection); + } while (argc > 0); } + if (nmc->complete) + return nmc->return_value; g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED, G_CALLBACK (connection_removed), nmc); return NMC_RESULT_SUCCESS; @@ -10561,11 +8380,12 @@ do_connection_reload (NmCli *nmc, int argc, char **argv) { GError *error = NULL; - nmc->return_value = NMC_RESULT_SUCCESS; + if (nmc->complete) + return nmc->return_value; if (!nm_client_reload_connections (nmc->client, NULL, &error)) { g_string_printf (nmc->return_text, _("Error: failed to reload connections: %s."), - error->message); + nmc_error_get_simple_message (error)); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; g_clear_error (&error); } @@ -10580,14 +8400,14 @@ do_connection_load (NmCli *nmc, int argc, char **argv) char **filenames, **failures = NULL; int i; - nmc->return_value = NMC_RESULT_SUCCESS; - if (argc == 0) { g_string_printf (nmc->return_text, _("Error: No connection specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; + return NMC_RESULT_ERROR_USER_INPUT; } + if (nmc->complete) + return NMC_RESULT_COMPLETE_FILE; + filenames = g_new (char *, argc + 1); for (i = 0; i < argc; i++) filenames[i] = argv[i]; @@ -10597,7 +8417,7 @@ do_connection_load (NmCli *nmc, int argc, char **argv) g_free (filenames); if (error) { g_string_printf (nmc->return_text, _("Error: failed to load connection: %s."), - error->message); + nmc_error_get_simple_message (error)); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; g_error_free (error); } @@ -10612,11 +8432,11 @@ do_connection_load (NmCli *nmc, int argc, char **argv) } // FIXME: change the text when non-VPN connection types are supported -#define PROMPT_IMPORT_TYPE PROMPT_VPN_TYPE -#define PROMPT_IMPORT_FILE _("File to import: ") +#define PROMPT_IMPORT_TYPE PROMPT_VPN_TYPE +#define PROMPT_IMPORT_FILE N_("File to import: ") static NMCResultCode -do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) +do_connection_import (NmCli *nmc, int argc, char **argv) { GError *error = NULL; const char *type = NULL, *filename = NULL; @@ -10624,11 +8444,16 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) AddConnectionInfo *info; NMConnection *connection = NULL; NMVpnEditorPlugin *plugin; + gs_free char *service_type = NULL; + gboolean temporary = FALSE; if (argc == 0) { + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + if (nmc->ask) { - type_ask = nmc_readline (PROMPT_IMPORT_TYPE); - filename_ask = nmc_readline (PROMPT_IMPORT_FILE); + type_ask = nmc_readline (gettext (PROMPT_IMPORT_TYPE)); + filename_ask = nmc_readline (gettext (PROMPT_IMPORT_FILE)); type = type_ask = type_ask ? g_strstrip (type_ask) : NULL; filename = filename_ask = filename_ask ? g_strstrip (filename_ask) : NULL; } else { @@ -10639,6 +8464,13 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) } while (argc > 0) { + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "temporary", "type", "file", NULL); + if (nmc_arg_is_option (*argv, "temporary")) { + temporary = TRUE; + next_arg (&argc, &argv); + } + if (strcmp (*argv, "type") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); @@ -10656,6 +8488,8 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto finish; } + if (argc == 1 && nmc->complete) + nmc->return_value = NMC_RESULT_COMPLETE_FILE; if (!filename) filename = *argv; else @@ -10670,6 +8504,9 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) argv++; } + if (nmc->complete) + goto finish; + if (!type) { g_string_printf (nmc->return_text, _("Error: 'type' argument is required.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; @@ -10681,8 +8518,15 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) goto finish; } + service_type = nm_vpn_plugin_info_list_find_service_type (nm_vpn_get_plugin_infos (), type); + if (!service_type) { + g_string_printf (nmc->return_text, _("Error: failed to find VPN plugin for %s."), type); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto finish; + } + /* Import VPN configuration */ - plugin = nm_vpn_lookup_plugin (type, NULL, &error); + plugin = nm_vpn_get_editor_plugin (service_type, &error); if (!plugin) { g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."), error->message); @@ -10709,7 +8553,7 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) add_connection_cb, info); - nmc->should_wait = TRUE; + nmc->should_wait++; finish: if (connection) g_object_unref (connection); @@ -10723,60 +8567,50 @@ static NMCResultCode do_connection_export (NmCli *nmc, int argc, char **argv) { NMConnection *connection = NULL; - const char *name; const char *out_name = NULL; char *name_ask = NULL; char *out_name_ask = NULL; const char *path = NULL; - const char *selector = NULL; const char *type = NULL; NMVpnEditorPlugin *plugin; GError *error = NULL; char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX"; + char **arg_arr = NULL; + int arg_num; + char ***argv_ptr = &argv; + int *argc_ptr = &argc; - if (argc == 0) { - if (nmc->ask) { - name_ask = nmc_readline (PROMPT_VPN_CONNECTION); - name = name_ask = name_ask ? g_strstrip (name_ask) : NULL; - out_name = out_name_ask = nmc_readline (_("Output file name: ")); - } else { - g_string_printf (nmc->return_text, _("Error: No arguments provided.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - } else { - if ( strcmp (*argv, "id") == 0 - || strcmp (*argv, "uuid") == 0 - || strcmp (*argv, "path") == 0) { + if (argc == 0 && nmc->ask) { + char *line; - selector = *argv; - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), - selector); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - } - name = *argv; - if (next_arg (&argc, &argv) == 0) - out_name = *argv; + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); - if (next_arg (&argc, &argv) == 0) { - g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } + line = nmc_readline ("%s: ", PROMPT_VPN_CONNECTION); + nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); + g_free (line); + argv_ptr = &arg_arr; + argc_ptr = &arg_num; } - if (!name) { - g_string_printf (nmc->return_text, _("Error: connection ID is missing.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, &error); + if (!connection) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = error->code; goto finish; } - connection = nmc_find_connection (nmc->connections, selector, name, NULL); - if (!connection) { - g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + + if (nmc->complete) + return nmc->return_value; + + if (next_arg (&argc, &argv) == 0) + out_name = *argv; + else if (nmc->ask) + out_name = out_name_ask = nmc_readline (_("Output file name: ")); + + if (next_arg (argc_ptr, argv_ptr) == 0) { + g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto finish; } @@ -10789,7 +8623,7 @@ do_connection_export (NmCli *nmc, int argc, char **argv) type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection)); /* Export VPN configuration */ - plugin = nm_vpn_lookup_plugin (type, NULL, &error); + plugin = nm_vpn_get_editor_plugin (type, &error); if (!plugin) { g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."), error->message); @@ -10841,35 +8675,6 @@ finish: return nmc->return_value; } - -typedef struct { - NmCli *nmc; - int argc; - char **argv; -} NmcEditorThreadData; - -static GThread *editor_thread; -static NmcEditorThreadData editor_thread_data; - -/* - * We need to run do_connection_edit() in a thread so that - * glib main loop is not blocked and could receive and process D-Bus - * return messages. - */ -static gpointer -connection_editor_thread_func (gpointer data) -{ - NmcEditorThreadData *td = (NmcEditorThreadData *) data; - - /* run editor for editing/adding connections */ - td->nmc->return_value = do_connection_edit (td->nmc, td->argc, td->argv); - - /* quit glib main loop now that we are done with this thread */ - quit (); - - return NULL; -} - static char * gen_func_connection_names (const char *text, int state) { @@ -10956,206 +8761,49 @@ nmcli_con_tab_completion (const char *text, int start, int end) return match_array; } -static GArray * -parse_preferred_connection_order (const char *order, GError **error) -{ - char **strv, **iter; - const char *str; - GArray *order_arr; - NmcSortOrder val; - gboolean inverse, unique; - int i; - - strv = nmc_strsplit_set (order, ":", -1); - if (!strv || !*strv) { - g_set_error (error, NMCLI_ERROR, 0, - _("incorrect string '%s' of '--order' option"), order); - g_strfreev (strv); - return NULL; - } - - order_arr = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), 4); - for (iter = strv; iter && *iter; iter++) { - str = *iter; - inverse = FALSE; - if (str[0] == '-') - inverse = TRUE; - if (str[0] == '+' || str[0] == '-') - str++; - - if (matches (str, "active") == 0) - val = inverse ? NMC_SORT_ACTIVE_INV : NMC_SORT_ACTIVE; - else if (matches (str, "name") == 0) - val = inverse ? NMC_SORT_NAME_INV : NMC_SORT_NAME; - else if (matches (str, "type") == 0) - val = inverse ? NMC_SORT_TYPE_INV : NMC_SORT_TYPE; - else if (matches (str, "path") == 0) - val = inverse ? NMC_SORT_PATH_INV : NMC_SORT_PATH; - else { - g_array_unref (order_arr); - order_arr = NULL; - g_set_error (error, NMCLI_ERROR, 0, - _("incorrect item '%s' in '--order' option"), *iter); - break; - } - /* Check for duplicates and ignore them. */ - unique = TRUE; - for (i = 0; i < order_arr->len; i++) { - if (abs (g_array_index (order_arr, NmcSortOrder, i)) - abs (val) == 0) { - unique = FALSE; - break; - } - } - - /* Value is ok and unique, add it to the array */ - if (unique) - g_array_append_val (order_arr, val); - } - - g_strfreev (strv); - return order_arr; -} +static const NMCCommand connection_cmds[] = { + {"show", do_connections_show, usage_connection_show }, + {"up", do_connection_up, usage_connection_up }, + {"down", do_connection_down, usage_connection_down }, + {"add", do_connection_add, usage_connection_add }, + {"edit", do_connection_edit, usage_connection_edit }, + {"delete", do_connection_delete, usage_connection_delete }, + {"reload", do_connection_reload, usage_connection_reload }, + {"load", do_connection_load, usage_connection_load }, + {"modify", do_connection_modify, usage_connection_modify }, + {"clone", do_connection_clone, usage_connection_clone }, + {"import", do_connection_import, usage_connection_import }, + {"export", do_connection_export, usage_connection_export }, + {"monitor", do_connection_monitor, usage_connection_monitor }, + {NULL, do_connections_show, usage }, +}; /* Entry point function for connections-related commands: 'nmcli connection' */ NMCResultCode do_connections (NmCli *nmc, int argc, char **argv) { - GError *error = NULL; - /* Register polkit agent */ nmc_start_polkit_agent_start_try (nmc); /* Set completion function for 'nmcli con' */ rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_tab_completion; - /* Exit early on help */ - if (nmc_arg_is_help (*argv)) { - usage (); - return nmc->return_value; - } - if (argc != 0 && nmc_arg_is_help (*(argv+1))) { - if (usage_connection_second_level (*argv)) - return nmc->return_value; - } - /* Get NMClient object early */ nmc->get_client (nmc); /* Check whether NetworkManager is running */ if (!nm_client_get_nm_running (nmc->client)) { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + if (!nmc->complete) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + } return nmc->return_value; } /* Get the connection list */ nmc->connections = nm_client_get_connections (nmc->client); - /* Now parse the command line and perform the required operation */ - if (argc == 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - nmc->return_value = do_connections_show (nmc, FALSE, FALSE, NULL, argc, argv); - } else { - if (matches (*argv, "show") == 0) { - gboolean active = FALSE; - gboolean show_secrets = FALSE; - GArray *order = NULL; - int i; - - next_arg (&argc, &argv); - /* check connection show options [--active] [--show-secrets] */ - for (i = 0; i < 3; i++) { - if (!active && nmc_arg_is_option (*argv, "active")) { - active = TRUE; - next_arg (&argc, &argv); - } - /* --show-secrets is deprecated in favour of global --show-secrets */ - /* Keep it here for backwards compatibility */ - if (!show_secrets && nmc_arg_is_option (*argv, "show-secrets")) { - show_secrets = TRUE; - next_arg (&argc, &argv); - } - if (!order && nmc_arg_is_option (*argv, "order")) { - if (next_arg (&argc, &argv) != 0) { - g_set_error_literal (&error, NMCLI_ERROR, 0, - _("'--order' argument is missing")); - goto opt_error; - } - order = parse_preferred_connection_order (*argv, &error); - if (error) - goto opt_error; - next_arg (&argc, &argv); - } - } - show_secrets = nmc->show_secrets || show_secrets; - nmc->return_value = do_connections_show (nmc, active, show_secrets, order, argc, argv); - if (order) - g_array_unref (order); - } else if (matches(*argv, "up") == 0) { - nmc->return_value = do_connection_up (nmc, argc-1, argv+1); - } else if (matches(*argv, "down") == 0) { - nmc->return_value = do_connection_down (nmc, argc-1, argv+1); - } else if (matches(*argv, "add") == 0) { - nmc->return_value = do_connection_add (nmc, argc-1, argv+1); - } else if (matches(*argv, "edit") == 0) { - nmc->should_wait++; - editor_thread_data.nmc = nmc; - editor_thread_data.argc = argc - 1; - editor_thread_data.argv = argv + 1; - editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data); - g_thread_unref (editor_thread); - } else if (matches(*argv, "delete") == 0) { - nmc->return_value = do_connection_delete (nmc, argc-1, argv+1); - } else if (matches(*argv, "reload") == 0) { - nmc->return_value = do_connection_reload (nmc, argc-1, argv+1); - } else if (matches(*argv, "load") == 0) { - nmc->return_value = do_connection_load (nmc, argc-1, argv+1); - } else if (matches (*argv, "modify") == 0) { - gboolean temporary = FALSE; - - next_arg (&argc, &argv); - if (nmc_arg_is_option (*argv, "temporary")) { - temporary = TRUE; - next_arg (&argc, &argv); - } - nmc->return_value = do_connection_modify (nmc, temporary, argc, argv); - } else if (matches (*argv, "clone") == 0) { - gboolean temporary = FALSE; - - next_arg (&argc, &argv); - if (nmc_arg_is_option (*argv, "temporary")) { - temporary = TRUE; - next_arg (&argc, &argv); - } - nmc->return_value = do_connection_clone (nmc, temporary, argc, argv); - } else if (matches(*argv, "import") == 0) { - gboolean temporary = FALSE; - - next_arg (&argc, &argv); - if (nmc_arg_is_option (*argv, "temporary")) { - temporary = TRUE; - next_arg (&argc, &argv); - } - nmc->return_value = do_connection_import (nmc, temporary, argc, argv); - } else if (matches(*argv, "export") == 0) { - nmc->return_value = do_connection_export (nmc, argc-1, argv+1); - } else if (matches(*argv, "monitor") == 0) { - nmc->return_value = do_connection_monitor (nmc, argc-1, argv+1); - } else { - usage (); - g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } - } - - return nmc->return_value; - -opt_error: - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - g_error_free (error); - return nmc->return_value; + return nmc_do_cmd (nmc, connection_cmds, *argv, argc, argv); } void diff --git a/clients/cli/connections.h b/clients/cli/connections.h index c158b634..c73178e2 100644 --- a/clients/cli/connections.h +++ b/clients/cli/connections.h @@ -26,4 +26,19 @@ NMCResultCode do_connections (NmCli *nmc, int argc, char **argv); void monitor_connections (NmCli *nmc); +gboolean +nmc_read_connection_properties (NmCli *nmc, + NMConnection *connection, + int *argc, + char ***argv, + GError **error); + +void nmc_active_connection_state_to_color (NMActiveConnectionState state, NmcTermColor *color); + +extern NmcOutputField nmc_fields_con_show[]; +extern NmcOutputField nmc_fields_settings_names[]; +extern NmcOutputField nmc_fields_con_active_details_general[]; +extern NmcOutputField nmc_fields_con_active_details_vpn[]; +extern NmcOutputField nmc_fields_con_active_details_groups[]; + #endif /* NMC_CONNECTIONS_H */ diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 1e3d9e42..afa769f6 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -30,13 +30,14 @@ #include "utils.h" #include "common.h" #include "devices.h" +#include "connections.h" /* define some prompts */ #define PROMPT_INTERFACE _("Interface: ") #define PROMPT_INTERFACES _("Interface(s): ") /* Available fields for 'device status' */ -static NmcOutputField nmc_fields_dev_status[] = { +NmcOutputField nmc_fields_dev_status[] = { {"DEVICE", N_("DEVICE")}, /* 0 */ {"TYPE", N_("TYPE")}, /* 1 */ {"STATE", N_("STATE")}, /* 2 */ @@ -51,7 +52,7 @@ static NmcOutputField nmc_fields_dev_status[] = { /* Available fields for 'device show' - GENERAL part */ -static NmcOutputField nmc_fields_dev_show_general[] = { +NmcOutputField nmc_fields_dev_show_general[] = { {"NAME", N_("NAME")}, /* 0 */ {"DEVICE", N_("DEVICE")}, /* 1 */ {"TYPE", N_("TYPE")}, /* 2 */ @@ -85,7 +86,7 @@ static NmcOutputField nmc_fields_dev_show_general[] = { #define NMC_FIELDS_DEV_SHOW_GENERAL_COMMON "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,HWADDR,STATE" /* Available fields for 'device show' - CONNECTIONS part */ -static NmcOutputField nmc_fields_dev_show_connections[] = { +NmcOutputField nmc_fields_dev_show_connections[] = { {"NAME", N_("NAME")}, /* 0 */ {"AVAILABLE-CONNECTION-PATHS", N_("AVAILABLE-CONNECTION-PATHS")}, /* 1 */ {"AVAILABLE-CONNECTIONS", N_("AVAILABLE-CONNECTIONS")}, /* 2 */ @@ -95,7 +96,7 @@ static NmcOutputField nmc_fields_dev_show_connections[] = { #define NMC_FIELDS_DEV_SHOW_CONNECTIONS_COMMON "AVAILABLE-CONNECTION-PATHS,AVAILABLE-CONNECTIONS" /* Available fields for 'device show' - CAPABILITIES part */ -static NmcOutputField nmc_fields_dev_show_cap[] = { +NmcOutputField nmc_fields_dev_show_cap[] = { {"NAME", N_("NAME")}, /* 0 */ {"CARRIER-DETECT", N_("CARRIER-DETECT")}, /* 1 */ {"SPEED", N_("SPEED")}, /* 2 */ @@ -106,7 +107,7 @@ static NmcOutputField nmc_fields_dev_show_cap[] = { #define NMC_FIELDS_DEV_SHOW_CAP_COMMON "NAME,CARRIER-DETECT,SPEED,IS-SOFTWARE" /* Available fields for 'device show' - wired properties part */ -static NmcOutputField nmc_fields_dev_show_wired_prop[] = { +NmcOutputField nmc_fields_dev_show_wired_prop[] = { {"NAME", N_("NAME")}, /* 0 */ {"CARRIER", N_("CARRIER")}, /* 1 */ {"S390-SUBCHANNELS", N_("S390-SUBCHANNELS")}, /* 2 */ @@ -116,7 +117,7 @@ static NmcOutputField nmc_fields_dev_show_wired_prop[] = { #define NMC_FIELDS_DEV_SHOW_WIRED_PROP_COMMON "NAME,CARRIER,S390-SUBCHANNELS" /* Available fields for 'device show' - wireless properties part */ -static NmcOutputField nmc_fields_dev_show_wifi_prop[] = { +NmcOutputField nmc_fields_dev_show_wifi_prop[] = { {"NAME", N_("NAME")}, /* 0 */ {"WEP", N_("WEP")}, /* 1 */ {"WPA", N_("WPA")}, /* 2 */ @@ -133,7 +134,7 @@ static NmcOutputField nmc_fields_dev_show_wifi_prop[] = { #define NMC_FIELDS_DEV_SHOW_WIFI_PROP_COMMON "NAME,WEP,WPA,WPA2,TKIP,CCMP,AP,ADHOC" /* Available fields for 'device show' - wimax properties part */ -static NmcOutputField nmc_fields_dev_show_wimax_prop[] = { +NmcOutputField nmc_fields_dev_show_wimax_prop[] = { {"NAME", N_("NAME")}, /* 0 */ {"CTR-FREQ", N_("CTR-FREQ")}, /* 1 */ {"RSSI", N_("RSSI")}, /* 2 */ @@ -146,7 +147,7 @@ static NmcOutputField nmc_fields_dev_show_wimax_prop[] = { #define NMC_FIELDS_DEV_SHOW_WIMAX_PROP_COMMON "NAME,CTR-FREQ,RSSI,CINR,TX-POW,BSID" /* Available fields for 'device wifi list' */ -static NmcOutputField nmc_fields_dev_wifi_list[] = { +NmcOutputField nmc_fields_dev_wifi_list[] = { {"NAME", N_("NAME")}, /* 0 */ {"SSID", N_("SSID")}, /* 1 */ {"SSID-HEX", N_("SSID-HEX")}, /* 2 */ @@ -172,7 +173,7 @@ static NmcOutputField nmc_fields_dev_wifi_list[] = { #define NMC_FIELDS_DEV_WIFI_LIST_FOR_DEV_LIST "NAME,"NMC_FIELDS_DEV_WIFI_LIST_COMMON /* Available fields for 'device wimax list' */ -static NmcOutputField nmc_fields_dev_wimax_list[] = { +NmcOutputField nmc_fields_dev_wimax_list[] = { {"NAME", N_("NAME")}, /* 0 */ {"NSP", N_("NSP")}, /* 1 */ {"SIGNAL", N_("SIGNAL")}, /* 2 */ @@ -186,8 +187,8 @@ static NmcOutputField nmc_fields_dev_wimax_list[] = { #define NMC_FIELDS_DEV_WIMAX_LIST_COMMON "NSP,SIGNAL,TYPE,DEVICE,ACTIVE" #define NMC_FIELDS_DEV_WIMAX_LIST_FOR_DEV_LIST "NAME,"NMC_FIELDS_DEV_WIMAX_LIST_COMMON -/* Available fields for 'device show' - BOND, TEAM, BRIDGE part */ -static NmcOutputField nmc_fields_dev_show_master_prop[] = { +/* Available fields for 'device show' - BOND, BRIDGE part */ +NmcOutputField nmc_fields_dev_show_master_prop[] = { {"NAME", N_("NAME")}, /* 0 */ {"SLAVES", N_("SLAVES")}, /* 1 */ {NULL, NULL} @@ -195,8 +196,18 @@ static NmcOutputField nmc_fields_dev_show_master_prop[] = { #define NMC_FIELDS_DEV_SHOW_MASTER_PROP_ALL "NAME,SLAVES" #define NMC_FIELDS_DEV_SHOW_MASTER_PROP_COMMON "NAME,SLAVES" +/* Available fields for 'device show' - TEAM part */ +NmcOutputField nmc_fields_dev_show_team_prop[] = { + {"NAME", N_("NAME")}, /* 0 */ + {"SLAVES", N_("SLAVES")}, /* 1 */ + {"CONFIG", N_("CONFIG")}, /* 2 */ + {NULL, NULL} +}; +#define NMC_FIELDS_DEV_SHOW_TEAM_PROP_ALL "NAME,SLAVES,CONFIG" +#define NMC_FIELDS_DEV_SHOW_TEAM_PROP_COMMON "NAME,SLAVES,CONFIG" + /* Available fields for 'device show' - VLAN part */ -static NmcOutputField nmc_fields_dev_show_vlan_prop[] = { +NmcOutputField nmc_fields_dev_show_vlan_prop[] = { {"NAME", N_("NAME")}, /* 0 */ {"PARENT", N_("PARENT")}, /* 1 */ {"ID", N_("ID")}, /* 2 */ @@ -206,7 +217,7 @@ static NmcOutputField nmc_fields_dev_show_vlan_prop[] = { #define NMC_FIELDS_DEV_SHOW_VLAN_PROP_COMMON "NAME,PARENT,ID" /* Available fields for 'device show' - BLUETOOTH part */ -static NmcOutputField nmc_fields_dev_show_bluetooth[] = { +NmcOutputField nmc_fields_dev_show_bluetooth[] = { {"NAME", N_("NAME")}, /* 0 */ {"CAPABILITIES", N_("CAPABILITIES")}, /* 1 */ {NULL, NULL} @@ -221,7 +232,7 @@ extern NmcOutputField nmc_fields_dhcp4_config[]; extern NmcOutputField nmc_fields_dhcp6_config[]; /* Available sections for 'device show' */ -static NmcOutputField nmc_fields_dev_show_sections[] = { +NmcOutputField nmc_fields_dev_show_sections[] = { {"GENERAL", N_("GENERAL"), 0, nmc_fields_dev_show_general + 1 }, /* 0 */ {"CAPABILITIES", N_("CAPABILITIES"), 0, nmc_fields_dev_show_cap + 1 }, /* 1 */ {"WIFI-PROPERTIES", N_("WIFI-PROPERTIES"), 0, nmc_fields_dev_show_wifi_prop + 1 }, /* 2 */ @@ -234,7 +245,7 @@ static NmcOutputField nmc_fields_dev_show_sections[] = { {"IP6", N_("IP6"), 0, nmc_fields_ip6_config + 1 }, /* 9 */ {"DHCP6", N_("DHCP6"), 0, nmc_fields_dhcp6_config + 1 }, /* 10 */ {"BOND", N_("BOND"), 0, nmc_fields_dev_show_master_prop + 1 }, /* 11 */ - {"TEAM", N_("TEAM"), 0, nmc_fields_dev_show_master_prop + 1 }, /* 12 */ + {"TEAM", N_("TEAM"), 0, nmc_fields_dev_show_team_prop + 1 }, /* 12 */ {"BRIDGE", N_("BRIDGE"), 0, nmc_fields_dev_show_master_prop + 1 }, /* 13 */ {"VLAN", N_("VLAN"), 0, nmc_fields_dev_show_vlan_prop + 1 }, /* 14 */ {"BLUETOOTH", N_("BLUETOOTH"), 0, nmc_fields_dev_show_bluetooth + 1 }, /* 15 */ @@ -247,7 +258,7 @@ static NmcOutputField nmc_fields_dev_show_sections[] = { "GENERAL.CONNECTION,GENERAL.CON-PATH,WIRED-PROPERTIES,IP4,IP6" /* Available fields for 'device lldp' */ -static NmcOutputField nmc_fields_dev_lldp_list[] = { +NmcOutputField nmc_fields_dev_lldp_list[] = { {"NAME", N_("NAME")}, /* 0 */ {"DEVICE", N_("DEVICE")}, /* 1 */ {"CHASSIS-ID", N_("CHASSIS-ID")}, /* 2 */ @@ -281,12 +292,13 @@ static void usage (void) { g_printerr (_("Usage: nmcli device { COMMAND | help }\n\n" - "COMMAND := { status | show | connect | reapply | disconnect | delete | monitor | wifi | lldp }\n\n" + "COMMAND := { status | show | set | connect | reapply | modify | disconnect | delete | monitor | wifi | lldp }\n\n" " status\n\n" " show [<ifname>]\n\n" " set [ifname] <ifname> [autoconnect yes|no] [managed yes|no]\n\n" " connect <ifname>\n\n" " reapply <ifname>\n\n" + " modify <ifname> ([+|-]<setting>.<property> <value>)+\n\n" " disconnect <ifname> ...\n\n" " delete <ifname> ...\n\n" " monitor <ifname> ...\n\n" @@ -349,6 +361,26 @@ usage_device_reapply (void) } static void +usage_device_modify (void) +{ + g_printerr (_("Usage: nmcli device modify { ARGUMENTS | --help }\n" + "\n" + "ARGUMENTS := <ifname> ([+|-]<setting>.<property> <value>)+\n" + "\n" + "Modify one or more properties currently active on the device without modifying\n" + "the connection profile. The changes have immediate effect. For multi-valued\n" + "properties you can use optional '+' or '-' prefix to the property name.\n" + "The '+' sign allows appending items instead of overwriting the whole value.\n" + "The '-' sign allows removing selected items instead of the whole value.\n" + "\n" + "Examples:\n" + "nmcli dev mod em1 ipv4.method manual ipv4.addr \"192.168.1.2/24, 10.10.1.5/8\"\n" + "nmcli dev mod em1 +ipv4.dns 8.8.4.4\n" + "nmcli dev mod em1 -ipv4.dns 1\n" + "nmcli dev mod em1 -ipv6.addr \"abbe::cafe/56\"\n")); +} + +static void usage_device_disconnect (void) { g_printerr (_("Usage: nmcli device disconnect { ARGUMENTS | help }\n" @@ -489,8 +521,8 @@ compare_devices (const void *a, const void *b) nm_device_get_iface (db)); } -static NMDevice ** -get_devices_sorted (NMClient *client) +NMDevice ** +nmc_get_devices_sorted (NMClient *client) { const GPtrArray *devs; NMDevice **sorted; @@ -505,8 +537,33 @@ get_devices_sorted (NMClient *client) return sorted; } +static void +complete_device (NMDevice **devices, const char *prefix, gboolean wifi_only) +{ + int i; + + for (i = 0; devices[i]; i++) { + const char *iface = nm_device_get_iface (devices[i]); + + if (wifi_only && !NM_IS_DEVICE_WIFI (devices[i])) + continue; + + if (g_str_has_prefix (iface, prefix)) + g_print ("%s\n", iface); + } +} + +void +nmc_complete_device (NMClient *client, const char *prefix, gboolean wifi_only) +{ + gs_free NMDevice **devices = NULL; + + devices = nmc_get_devices_sorted (client); + complete_device (devices, prefix, wifi_only); +} + static GSList * -device_list (NmCli *nmc, int argc, char **argv) +get_device_list (NmCli *nmc, int argc, char **argv) { int arg_num = argc; char **arg_arr = NULL; @@ -530,8 +587,11 @@ device_list (NmCli *nmc, int argc, char **argv) } } - devices = get_devices_sorted (nmc->client); + devices = nmc_get_devices_sorted (nmc->client); while (arg_num > 0) { + if (arg_num == 1 && nmc->complete) + complete_device (devices, *arg_ptr, FALSE); + device = NULL; for (i = 0; devices[i]; i++) { if (!g_strcmp0 (nm_device_get_iface (devices[i]), *arg_ptr)) { @@ -546,7 +606,8 @@ device_list (NmCli *nmc, int argc, char **argv) else g_printerr (_("Warning: argument '%s' is duplicated.\n"), *arg_ptr); } else { - g_printerr (_("Error: Device '%s' not found.\n"), *arg_ptr); + if (!nmc->complete) + g_printerr (_("Error: Device '%s' not found.\n"), *arg_ptr); g_string_printf (nmc->return_text, _("Error: not all devices found.")); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; } @@ -562,6 +623,45 @@ error: return queue; } +static NMDevice * +get_device (NmCli *nmc, int *argc, char ***argv, GError **error) +{ + gs_free NMDevice **devices = NULL; + gs_free char *ifname_ask = NULL; + const char *ifname = NULL; + int i; + + if (*argc == 0) { + if (nmc->ask) + ifname = ifname_ask = nmc_readline (PROMPT_INTERFACE); + + if (!ifname_ask) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("No interface specified")); + return NULL; + } + } else { + ifname = **argv; + next_arg (argc, argv); + } + + devices = nmc_get_devices_sorted (nmc->client); + for (i = 0; devices[i]; i++) { + if (!g_strcmp0 (nm_device_get_iface (devices[i]), ifname)) + break; + } + + if (nmc->complete && !*argc) + complete_device (devices, ifname, FALSE); + + if (devices[i] == NULL) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, + _("Device '%s' not found"), ifname); + } + + return devices[i]; +} + static int compare_aps (gconstpointer a, gconstpointer b, gpointer user_data) { @@ -595,27 +695,22 @@ sort_access_points (const GPtrArray *aps) return sorted; } -typedef struct { - NmcTermColor color; - NmcTermFormat color_fmt; -} ColorInfo; - -static ColorInfo -wifi_signal_to_color (guint8 strength) +static void +wifi_signal_to_color (guint8 strength, NmcTermColor *color, NmcTermFormat *color_fmt) { - ColorInfo color_info = { NMC_TERM_COLOR_NORMAL, NMC_TERM_FORMAT_NORMAL }; + *color = NMC_TERM_COLOR_NORMAL; + *color_fmt = NMC_TERM_FORMAT_NORMAL; if (strength > 80) - color_info.color = NMC_TERM_COLOR_GREEN; + *color = NMC_TERM_COLOR_GREEN; else if (strength > 55) - color_info.color = NMC_TERM_COLOR_YELLOW; + *color = NMC_TERM_COLOR_YELLOW; else if (strength > 30) - color_info.color = NMC_TERM_COLOR_MAGENTA; + *color = NMC_TERM_COLOR_MAGENTA; else if (strength > 5) - color_info.color = NMC_TERM_COLOR_CYAN; + *color = NMC_TERM_COLOR_CYAN; else - color_info.color_fmt = NMC_TERM_FORMAT_DIM; - return color_info; + *color_fmt = NMC_TERM_FORMAT_DIM; } static char * @@ -687,7 +782,8 @@ fill_output_access_point (gpointer data, gpointer user_data) GString *security_str; char *ap_name; const char *sig_bars; - ColorInfo color_info; + NmcTermColor color; + NmcTermFormat color_fmt; if (info->active_bssid) { const char *current_bssid = nm_access_point_get_bssid (ap); @@ -774,9 +870,9 @@ fill_output_access_point (gpointer data, gpointer user_data) set_val_strc (arr, 16, nm_object_get_path (NM_OBJECT (ap))); /* Set colors */ - color_info = wifi_signal_to_color (strength); - set_val_color_all (arr, color_info.color); - set_val_color_fmt_all (arr, color_info.color_fmt); + wifi_signal_to_color (strength, &color, &color_fmt); + set_val_color_all (arr, color); + set_val_color_fmt_all (arr, color_fmt); if (active) arr[15].color = NMC_TERM_COLOR_GREEN; @@ -840,10 +936,10 @@ get_active_connection_id (NMDevice *device) } static gboolean -print_bond_team_bridge_info (NMDevice *device, - NmCli *nmc, - const char *group_prefix, - const char *one_field) +print_bond_bridge_info (NMDevice *device, + NmCli *nmc, + const char *group_prefix, + const char *one_field) { const GPtrArray *slaves = NULL; GString *slaves_str; @@ -853,10 +949,10 @@ print_bond_team_bridge_info (NMDevice *device, if (NM_IS_DEVICE_BOND (device)) slaves = nm_device_bond_get_slaves (NM_DEVICE_BOND (device)); - else if (NM_IS_DEVICE_TEAM (device)) - slaves = nm_device_team_get_slaves (NM_DEVICE_TEAM (device)); else if (NM_IS_DEVICE_BRIDGE (device)) slaves = nm_device_bridge_get_slaves (NM_DEVICE_BRIDGE (device)); + else + g_return_val_if_reached (FALSE); slaves_str = g_string_new (NULL); for (idx = 0; slaves && idx < slaves->len; idx++) { @@ -891,6 +987,76 @@ print_bond_team_bridge_info (NMDevice *device, return TRUE; } +static char * +sanitize_team_config (const char *config) +{ + char *ret; + int i; + + if (!config) + return NULL; + + ret = g_strdup (config); + + for (i = 0; i < strlen (ret); i++) { + if (ret[i] == '\n') + ret[i] = ' '; + } + + return ret; +} + +static gboolean +print_team_info (NMDevice *device, + NmCli *nmc, + const char *group_prefix, + const char *one_field) +{ + const GPtrArray *slaves = NULL; + GString *slaves_str; + int idx; + NmcOutputField *tmpl, *arr; + size_t tmpl_len; + + if (NM_IS_DEVICE_TEAM (device)) + slaves = nm_device_team_get_slaves (NM_DEVICE_TEAM (device)); + else + g_return_val_if_reached (FALSE); + + slaves_str = g_string_new (NULL); + for (idx = 0; slaves && idx < slaves->len; idx++) { + NMDevice *slave = g_ptr_array_index (slaves, idx); + const char *iface = nm_device_get_iface (slave); + + if (iface) { + g_string_append (slaves_str, iface); + g_string_append_c (slaves_str, ' '); + } + } + if (slaves_str->len > 0) + g_string_truncate (slaves_str, slaves_str->len-1); /* Chop off last space */ + + tmpl = nmc_fields_dev_show_team_prop; + tmpl_len = sizeof (nmc_fields_dev_show_team_prop); + nmc->print_fields.indices = parse_output_fields (one_field ? one_field : NMC_FIELDS_DEV_SHOW_TEAM_PROP_ALL, + tmpl, FALSE, NULL, NULL); + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (nmc->output_data, arr); + + arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); + set_val_strc (arr, 0, group_prefix); /* TEAM */ + set_val_str (arr, 1, slaves_str->str); + set_val_str (arr, 2, sanitize_team_config (nm_device_team_get_config (NM_DEVICE_TEAM (device)))); + g_ptr_array_add (nmc->output_data, arr); + + print_data (nmc); /* Print all data */ + + g_string_free (slaves_str, FALSE); + nmc_empty_output_fields (nmc); + + return TRUE; +} + static gboolean show_device_info (NMDevice *device, NmCli *nmc) { @@ -1146,19 +1312,19 @@ show_device_info (NMDevice *device, NmCli *nmc) /* Bond specific information */ if (NM_IS_DEVICE_BOND (device)) { if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[11].name)) - was_output = print_bond_team_bridge_info (device, nmc, nmc_fields_dev_show_sections[11].name, section_fld); + was_output = print_bond_bridge_info (device, nmc, nmc_fields_dev_show_sections[11].name, section_fld); } /* Team specific information */ if (NM_IS_DEVICE_TEAM (device)) { if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[12].name)) - was_output = print_bond_team_bridge_info (device, nmc, nmc_fields_dev_show_sections[12].name, section_fld); + was_output = print_team_info (device, nmc, nmc_fields_dev_show_sections[12].name, section_fld); } /* Bridge specific information */ if (NM_IS_DEVICE_BRIDGE (device)) { if (!strcasecmp (nmc_fields_dev_show_sections[section_idx].name, nmc_fields_dev_show_sections[13].name)) - was_output = print_bond_team_bridge_info (device, nmc, nmc_fields_dev_show_sections[13].name, section_fld); + was_output = print_bond_bridge_info (device, nmc, nmc_fields_dev_show_sections[13].name, section_fld); } /* VLAN-specific information */ @@ -1264,20 +1430,20 @@ show_device_info (NMDevice *device, NmCli *nmc) return TRUE; } -static ColorInfo -device_state_to_color (NMDeviceState state) +void +nmc_device_state_to_color (NMDeviceState state, NmcTermColor *color, NmcTermFormat *color_fmt) { - ColorInfo color_info = { NMC_TERM_COLOR_NORMAL, NMC_TERM_FORMAT_NORMAL }; + *color = NMC_TERM_COLOR_NORMAL; + *color_fmt = NMC_TERM_FORMAT_NORMAL; if (state <= NM_DEVICE_STATE_UNAVAILABLE) - color_info.color_fmt= NMC_TERM_FORMAT_DIM; + *color_fmt= NMC_TERM_FORMAT_DIM; else if (state == NM_DEVICE_STATE_DISCONNECTED) - color_info.color = NMC_TERM_COLOR_RED; + *color = NMC_TERM_COLOR_RED; else if (state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_SECONDARIES) - color_info.color = NMC_TERM_COLOR_YELLOW; + *color = NMC_TERM_COLOR_YELLOW; else if (state == NM_DEVICE_STATE_ACTIVATED) - color_info.color = NMC_TERM_COLOR_GREEN; - return color_info; + *color = NMC_TERM_COLOR_GREEN; } static void @@ -1285,7 +1451,8 @@ fill_output_device_status (NMDevice *device, NmCli *nmc) { NMActiveConnection *ac; NMDeviceState state; - ColorInfo color_info; + NmcTermColor color; + NmcTermFormat color_fmt; NmcOutputField *arr = nmc_dup_fields_array (nmc_fields_dev_status, sizeof (nmc_fields_dev_status), 0); @@ -1294,9 +1461,9 @@ fill_output_device_status (NMDevice *device, NmCli *nmc) ac = nm_device_get_active_connection (device); /* Show devices in color */ - color_info = device_state_to_color (state); - set_val_color_all (arr, color_info.color); - set_val_color_fmt_all (arr, color_info.color_fmt); + nmc_device_state_to_color (state, &color, &color_fmt); + set_val_color_all (arr, color); + set_val_color_fmt_all (arr, color_fmt); set_val_strc (arr, 0, nm_device_get_iface (device)); set_val_strc (arr, 1, nm_device_get_type_description (device)); @@ -1321,6 +1488,16 @@ do_devices_status (NmCli *nmc, int argc, char **argv) NmcOutputField *tmpl, *arr; size_t tmpl_len; + /* Nothing to complete */ + if (nmc->complete) + return nmc->return_value; + + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + g_error_free (error); + return NMC_RESULT_ERROR_USER_INPUT; + } + while (argc > 0) { g_printerr (_("Unknown parameter: %s\n"), *argv); argc--; @@ -1341,8 +1518,7 @@ do_devices_status (NmCli *nmc, int argc, char **argv) if (error) { g_string_printf (nmc->return_text, _("Error: 'device status': %s"), error->message); g_error_free (error); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } /* Add headers */ @@ -1350,7 +1526,7 @@ do_devices_status (NmCli *nmc, int argc, char **argv) arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); g_ptr_array_add (nmc->output_data, arr); - devices = get_devices_sorted (nmc->client); + devices = nmc_get_devices_sorted (nmc->client); for (i = 0; devices[i]; i++) fill_output_device_status (devices[i], nmc); @@ -1360,59 +1536,53 @@ do_devices_status (NmCli *nmc, int argc, char **argv) g_free (devices); return NMC_RESULT_SUCCESS; - -error: - return nmc->return_value; } static NMCResultCode -do_devices_show (NmCli *nmc, int argc, char **argv) +do_device_show (NmCli *nmc, int argc, char **argv) { - NMDevice **devices = NULL; - NMDevice *device = NULL; - const char *ifname = NULL; - int i; - gboolean ret; - - if (argc == 1) - ifname = *argv; - else if (argc > 1) { - g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *(argv+1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } + gs_free_error GError *error = NULL; - devices = get_devices_sorted (nmc->client); + if (!nmc->mode_specified) + nmc->multiline_output = TRUE; /* multiline mode is default for 'device show' */ - if (ifname) { - /* Interface specified; show details only for the device */ - for (i = 0; devices[i]; i++) { - NMDevice *candidate = devices[i]; - const char *dev_iface = nm_device_get_iface (candidate); + if (argc) { + NMDevice *device; - if (!g_strcmp0 (dev_iface, ifname)) - device = candidate; - } + device = get_device (nmc, &argc, &argv, &error); if (!device) { - g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return error->code; } + + if (argc) { + g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv); + return NMC_RESULT_ERROR_USER_INPUT; + } + + if (nmc->complete) + return nmc->return_value; + show_device_info (device, nmc); } else { + NMDevice **devices = nmc_get_devices_sorted (nmc->client); + int i; + + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + /* Show details for all devices */ for (i = 0; devices[i]; i++) { nmc_empty_output_fields (nmc); - ret = show_device_info (devices[i], nmc); - if (!ret) + if (!show_device_info (devices[i], nmc)) break; if (devices[i + 1]) g_print ("\n"); /* Empty line */ } + + g_free (devices); } -error: - g_free (devices); return nmc->return_value; } @@ -1657,57 +1827,27 @@ connect_device_cb (GObject *client, GAsyncResult *result, gpointer user_data) static NMCResultCode do_device_connect (NmCli *nmc, int argc, char **argv) { - NMDevice **devices; NMDevice *device = NULL; - const char *ifname = NULL; - char *ifname_ask = NULL; - int i; AddAndActivateInfo *info; + gs_free_error GError *error = NULL; /* Set default timeout for connect operation. */ if (nmc->timeout == -1) nmc->timeout = 90; - if (argc == 0) { - if (nmc->ask) - ifname = ifname_ask = nmc_readline (PROMPT_INTERFACE); - - if (!ifname_ask) { - g_string_printf (nmc->return_text, _("Error: No interface specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - } else { - ifname = *argv; - } - - if (!ifname) { - g_string_printf (nmc->return_text, _("Error: No interface specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + device = get_device (nmc, &argc, &argv, &error); + if (!device) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return error->code; } - if (next_arg (&argc, &argv) == 0) { + if (*argv) { g_string_printf (nmc->return_text, _("Error: extra argument not allowed: '%s'."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - - devices = get_devices_sorted (nmc->client); - for (i = 0; devices[i]; i++) { - NMDevice *candidate = devices[i]; - const char *dev_iface = nm_device_get_iface (candidate); - - if (!g_strcmp0 (dev_iface, ifname)) - device = candidate; + return NMC_RESULT_ERROR_USER_INPUT; } - g_free (devices); - if (!device) { - g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; - } + if (nmc->complete) + return nmc->return_value; /* * Use nowait_flag instead of should_wait, because exiting has to be postponed @@ -1740,9 +1880,6 @@ do_device_connect (NmCli *nmc, int argc, char **argv) if (nmc->print_output == NMC_PRINT_PRETTY) progress_id = g_timeout_add (120, progress_cb, device); -error: - g_free (ifname_ask); - return nmc->return_value; } @@ -1856,55 +1993,30 @@ reapply_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) static NMCResultCode do_device_reapply (NmCli *nmc, int argc, char **argv) { - gs_free NMDevice **devices = NULL; - NMDevice *device = NULL; + NMDevice *device; DeviceCbInfo *info = NULL; - char **arg_ptr = argv; - int arg_num = argc; - int i; - gs_free char *device_name_free = NULL; - const char *device_name = NULL; + gs_free_error GError *error = NULL; /* Set default timeout for reapply operation. */ if (nmc->timeout == -1) nmc->timeout = 10; - if (argc == 0) { - if (nmc->ask) { - device_name_free = nmc_readline (PROMPT_INTERFACE); - device_name = device_name_free; - } - if (!device_name) { - g_string_printf (nmc->return_text, _("Error: No interface specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; - } - } else if (argc == 1) { - device_name = arg_ptr[0]; - next_arg (&arg_num, &arg_ptr); - } else { - next_arg (&arg_num, &arg_ptr); - g_string_printf (nmc->return_text, _("Error: unsupported argument '%s'."), *arg_ptr); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; + device = get_device (nmc, &argc, &argv, &error); + if (!device) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return error->code; } - devices = get_devices_sorted (nmc->client); - for (i = 0; devices[i]; i++) { - if (!g_strcmp0 (nm_device_get_iface (devices[i]), device_name)) { - device = devices[i]; - break; - } + if (argc) { + g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv); + return NMC_RESULT_ERROR_USER_INPUT; } - if (!device) { - g_string_printf (nmc->return_text, _("Error: device '%s' not found."), device_name); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + if (nmc->complete) return nmc->return_value; - } nmc->nowait_flag = (nmc->timeout == 0); - nmc->should_wait = TRUE; + nmc->should_wait++; info = g_slice_new0 (DeviceCbInfo); info->nmc = nmc; @@ -1916,6 +2028,108 @@ do_device_reapply (NmCli *nmc, int argc, char **argv) return nmc->return_value; } +typedef struct { + NmCli *nmc; + int argc; + char **argv; +} ModifyInfo; + +static void +modify_reapply_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (object); + ModifyInfo *info = user_data; + NmCli *nmc = info->nmc; + GError *error = NULL; + + if (!nm_device_reapply_finish (device, result, &error)) { + g_string_printf (nmc->return_text, _("Error: Reapplying connection to device '%s' (%s) failed: %s"), + nm_device_get_iface (device), + nm_object_get_path (NM_OBJECT (device)), + error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_DEV_DISCONNECT; + } else { + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + g_print (_("Connection successfully reapplied to device '%s'.\n"), + nm_device_get_iface (device)); + } + + g_slice_free (ModifyInfo, info); + quit (); +} + +static void +modify_get_applied_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (object); + ModifyInfo *info = user_data; + NmCli *nmc = info->nmc; + gs_free_error GError *error = NULL; + NMConnection *connection; + guint64 version_id; + + connection = nm_device_get_applied_connection_finish (device, + result, + &version_id, + &error); + if (!connection) { + g_string_printf (nmc->return_text, _("Error: Reading applied connection from device '%s' (%s) failed: %s"), + nm_device_get_iface (device), + nm_object_get_path (NM_OBJECT (device)), + error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_slice_free (ModifyInfo, info); + quit (); + return; + } + + if (!nmc_read_connection_properties (info->nmc, connection, &info->argc, &info->argv, &error)) { + g_string_assign (nmc->return_text, error->message); + nmc->return_value = error->code; + g_slice_free (ModifyInfo, info); + quit (); + return; + } + + if (nmc->complete) + quit (); + else + nm_device_reapply_async (device, connection, version_id, 0, NULL, modify_reapply_cb, info); +} + +static NMCResultCode +do_device_modify (NmCli *nmc, int argc, char **argv) +{ + NMDevice *device = NULL; + ModifyInfo *info = NULL; + gs_free_error GError *error = NULL; + + device = get_device (nmc, &argc, &argv, &error); + if (!device) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return error->code; + } + + if (nmc->timeout == -1) + nmc->timeout = 10; + + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait++; + + info = g_slice_new0 (ModifyInfo); + info->nmc = nmc; + info->argc = argc; + info->argv = argv; + + nm_device_get_applied_connection_async (device, 0, NULL, modify_get_applied_cb, info); + + return nmc->return_value; +} + static void disconnect_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) { @@ -1950,7 +2164,7 @@ disconnect_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) } static NMCResultCode -do_device_disconnect (NmCli *nmc, int argc, char **argv) +do_devices_disconnect (NmCli *nmc, int argc, char **argv) { NMDevice *device; DeviceCbInfo *info = NULL; @@ -1960,9 +2174,11 @@ do_device_disconnect (NmCli *nmc, int argc, char **argv) if (nmc->timeout == -1) nmc->timeout = 10; - queue = device_list (nmc, argc, argv); + queue = get_device_list (nmc, argc, argv); if (!queue) - goto error; + return nmc->return_value; + if (nmc->complete) + goto out; queue = g_slist_reverse (queue); info = g_slice_new0 (DeviceCbInfo); @@ -1988,7 +2204,7 @@ do_device_disconnect (NmCli *nmc, int argc, char **argv) nm_device_disconnect_async (device, NULL, disconnect_device_cb, info); } -error: +out: g_slist_free (queue); return nmc->return_value; } @@ -2017,7 +2233,7 @@ delete_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) } static NMCResultCode -do_device_delete (NmCli *nmc, int argc, char **argv) +do_devices_delete (NmCli *nmc, int argc, char **argv) { NMDevice *device; DeviceCbInfo *info = NULL; @@ -2027,9 +2243,11 @@ do_device_delete (NmCli *nmc, int argc, char **argv) if (nmc->timeout == -1) nmc->timeout = 10; - queue = device_list (nmc, argc, argv); + queue = get_device_list (nmc, argc, argv); if (!queue) - goto error; + return nmc->return_value; + if (nmc->complete) + goto out; queue = g_slist_reverse (queue); info = g_slice_new0 (DeviceCbInfo); @@ -2052,7 +2270,7 @@ do_device_delete (NmCli *nmc, int argc, char **argv) nm_device_delete_async (device, NULL, delete_device_cb, info); } -error: +out: g_slist_free (queue); return nmc->return_value; } @@ -2062,9 +2280,7 @@ do_device_set (NmCli *nmc, int argc, char **argv) { #define DEV_SET_AUTOCONNECT 0 #define DEV_SET_MANAGED 1 - NMDevice **devices; NMDevice *device = NULL; - const char *ifname = NULL; int i; struct { int idx; @@ -2073,63 +2289,42 @@ do_device_set (NmCli *nmc, int argc, char **argv) [DEV_SET_AUTOCONNECT] = { -1 }, [DEV_SET_MANAGED] = { -1 }, }; + gs_free_error GError *error = NULL; if (argc >= 1 && g_strcmp0 (*argv, "ifname") == 0) { argc--; argv++; } - if (argc == 0) { - g_string_printf (nmc->return_text, _("Error: No interface specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } else - ifname = *argv; - - if (!ifname) { - g_string_printf (nmc->return_text, _("Error: No interface specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - - devices = get_devices_sorted (nmc->client); - for (i = 0; devices[i]; i++) { - NMDevice *candidate = devices[i]; - const char *dev_iface = nm_device_get_iface (candidate); - - if (!g_strcmp0 (dev_iface, ifname)) - device = candidate; - } - g_free (devices); - + device = get_device (nmc, &argc, &argv, &error); if (!device) { - g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return error->code; } - if (argc == 1) { + if (!argc) { g_string_printf (nmc->return_text, _("Error: No property specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } i = 0; - while (next_arg (&argc, &argv) == 0) { + do { gboolean flag; - gs_free_error GError *tmp_err = NULL; + + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "managed", "autoconnect", NULL); if (matches (*argv, "managed") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: '%s' argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } - if (!nmc_string_to_bool (*argv, &flag, &tmp_err)) { + if (argc == 1 && nmc->complete) + nmc_complete_bool (*argv); + if (!nmc_string_to_bool (*argv, &flag, &error)) { g_string_printf (nmc->return_text, _("Error: 'managed': %s."), - tmp_err->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + error->message); + return NMC_RESULT_ERROR_USER_INPUT; } values[DEV_SET_MANAGED].idx = ++i; values[DEV_SET_MANAGED].value = flag; @@ -2137,25 +2332,26 @@ do_device_set (NmCli *nmc, int argc, char **argv) else if (matches (*argv, "autoconnect") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: '%s' argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } - if (!nmc_string_to_bool (*argv, &flag, &tmp_err)) { + if (argc == 1 && nmc->complete) + nmc_complete_bool (*argv); + if (!nmc_string_to_bool (*argv, &flag, &error)) { g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."), - tmp_err->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + error->message); + return NMC_RESULT_ERROR_USER_INPUT; } values[DEV_SET_AUTOCONNECT].idx = ++i; values[DEV_SET_AUTOCONNECT].value = flag; } else { - usage_device_set (); g_string_printf (nmc->return_text, _("Error: property '%s' is not known."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } - } + } while (next_arg (&argc, &argv) == 0); + + if (nmc->complete) + return nmc->return_value; /* when multiple properties are specified, set them in the order as they * are specified on the command line. */ @@ -2170,8 +2366,6 @@ do_device_set (NmCli *nmc, int argc, char **argv) if (values[DEV_SET_MANAGED].idx >= 0) nm_device_set_managed (device, values[DEV_SET_MANAGED].value); -error: - quit (); return nmc->return_value; } @@ -2179,10 +2373,14 @@ static void device_state (NMDevice *device, GParamSpec *pspec, NmCli *nmc) { NMDeviceState state = nm_device_get_state (device); - ColorInfo color = device_state_to_color (state); - char *str = nmc_colorize (nmc, color.color, color.color_fmt, "%s: %s\n", - nm_device_get_iface (device), - nmc_device_state_to_string (state)); + NmcTermColor color; + NmcTermFormat color_fmt; + char *str; + + nmc_device_state_to_color (state, &color, &color_fmt); + str = nmc_colorize (nmc, color, color_fmt, "%s: %s\n", + nm_device_get_iface (device), + nmc_device_state_to_string (state)); g_print ("%s", str); g_free (str); @@ -2235,8 +2433,14 @@ device_removed (NMClient *client, NMDevice *device, NmCli *nmc) } static NMCResultCode -do_device_monitor (NmCli *nmc, int argc, char **argv) +do_devices_monitor (NmCli *nmc, int argc, char **argv) { + GSList *queue = get_device_list (nmc, argc, argv); + GSList *iter; + + if (nmc->complete) + return nmc->return_value; + if (argc == 0) { /* No devices specified. Monitor all. */ const GPtrArray *devices = nm_client_get_devices (nmc->client); @@ -2249,21 +2453,14 @@ do_device_monitor (NmCli *nmc, int argc, char **argv) nmc->should_wait++; g_signal_connect (nmc->client, NM_CLIENT_DEVICE_ADDED, G_CALLBACK (device_added), nmc); } else { - /* Monitor just the specified devices. */ - GSList *queue = device_list (nmc, argc, argv); - GSList *iter; - - if (!queue) - return nmc->return_value; - + /* Monitor the specified devices. */ for (iter = queue; iter; iter = g_slist_next (iter)) device_watch (nmc, NM_DEVICE (iter->data)); g_slist_free (queue); } g_signal_connect (nmc->client, NM_CLIENT_DEVICE_REMOVED, G_CALLBACK (device_removed), nmc); - - return NMC_RESULT_SUCCESS; + return nmc->return_value; } static void @@ -2299,6 +2496,118 @@ show_access_point_info (NMDevice *device, NmCli *nmc) g_free (info); } +/* + * Find a Wi-Fi device with 'iface' in 'devices' array. If 'iface' is NULL, + * the first Wi-Fi device is returned. 'idx' parameter is updated to the point + * where the function finished so that the function can be called repeatedly + * to get next matching device. + * Returns: found device or NULL + */ +static NMDevice * +find_wifi_device_by_iface (NMDevice **devices, const char *iface, int *idx) +{ + int i; + + for (i = idx ? *idx : 0; devices[i]; i++) { + const char *dev_iface = nm_device_get_iface (devices[i]); + + if (!NM_IS_DEVICE_WIFI (devices[i])) + continue; + + if (iface) { + /* If a iface was specified then use it. */ + if (g_strcmp0 (dev_iface, iface) == 0) + break; + } else { + /* Else return the first Wi-Fi device. */ + break; + } + } + + if (idx) + *idx = i + 1; + return devices[i]; +} + +/* + * Find AP on 'device' according to 'bssid' or 'ssid' parameter. + * Returns: found AP or NULL + */ +static NMAccessPoint * +find_ap_on_device (NMDevice *device, const char *bssid, const char *ssid, gboolean complete) +{ + const GPtrArray *aps; + NMAccessPoint *ap = NULL; + int i; + + g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL); + + aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device)); + for (i = 0; i < aps->len; i++) { + NMAccessPoint *candidate_ap = g_ptr_array_index (aps, i); + + if (bssid) { + /* Parameter is BSSID */ + const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); + + /* Compare BSSIDs */ + if (complete) { + if (g_str_has_prefix (candidate_bssid, bssid)) + g_print ("%s\n", candidate_bssid); + } else if (strcmp (bssid, candidate_bssid) == 0) { + ap = candidate_ap; + break; + } + } + + if (ssid) { + /* Parameter is SSID */ + GBytes *candidate_ssid; + char *ssid_tmp; + + candidate_ssid = nm_access_point_get_ssid (candidate_ap); + if (!candidate_ssid) + continue; + + ssid_tmp = nm_utils_ssid_to_utf8 (g_bytes_get_data (candidate_ssid, NULL), + g_bytes_get_size (candidate_ssid)); + + /* Compare SSIDs */ + if (complete) { + if (g_str_has_prefix (ssid_tmp, ssid)) + g_print ("%s\n", ssid_tmp); + } else if (strcmp (ssid, ssid_tmp) == 0) { + ap = candidate_ap; + g_free (ssid_tmp); + break; + } + g_free (ssid_tmp); + } + } + + return ap; +} + +static void +complete_aps (NMDevice **devices, const char *ifname, + const char *bssid_prefix, const char *ssid_prefix) +{ + int devices_idx = 0; + NMDevice *device; + + while ((device = find_wifi_device_by_iface (devices, ifname, &devices_idx))) + find_ap_on_device (device, bssid_prefix, ssid_prefix, TRUE); +} + +void +nmc_complete_bssid (NMClient *client, const char *ifname, const char *bssid_prefix) +{ + gs_free NMDevice **devices = NULL; + + devices = nmc_get_devices_sorted (client); + complete_aps (devices, ifname, bssid_prefix, NULL); +} + static NMCResultCode do_device_wifi_list (NmCli *nmc, int argc, char **argv) { @@ -2307,7 +2616,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) NMAccessPoint *ap = NULL; const char *ifname = NULL; const char *bssid_user = NULL; - NMDevice **devices = NULL; + gs_free NMDevice **devices = NULL; const GPtrArray *aps; APInfo *info; int i, j; @@ -2318,23 +2627,29 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) size_t tmpl_len; const char *base_hdr = _("Wi-Fi scan list"); + devices = nmc_get_devices_sorted (nmc->client); + while (argc > 0) { + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "ifname", "bssid", NULL); + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } ifname = *argv; + complete_device (devices, ifname, TRUE); } else if (strcmp (*argv, "bssid") == 0 || strcmp (*argv, "hwaddr") == 0) { /* hwaddr is deprecated and will be removed later */ if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } bssid_user = *argv; - } else { + if (argc == 1 && nmc->complete) + complete_aps (devices, NULL, bssid_user, NULL); + } else if (!nmc->complete) { g_printerr (_("Unknown parameter: %s\n"), *argv); } @@ -2356,28 +2671,18 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) if (error) { g_string_printf (nmc->return_text, _("Error: 'device wifi': %s"), error->message); g_error_free (error); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } - devices = get_devices_sorted (nmc->client); - if (ifname) { - /* Device specified - list only APs of this interface */ - for (i = 0; devices[i]; i++) { - NMDevice *candidate = devices[i]; - const char *dev_iface = nm_device_get_iface (candidate); + if (nmc->complete) + return nmc->return_value; - if (!g_strcmp0 (dev_iface, ifname)) { - device = candidate; - break; - } - } + if (ifname) { + device = find_wifi_device_by_iface (devices, ifname, NULL); if (!device) { g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + return NMC_RESULT_ERROR_NOT_FOUND; } - /* Main header name */ nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, ifname); @@ -2398,8 +2703,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) if (!ap) { g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), bssid_user); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + return NMC_RESULT_ERROR_NOT_FOUND; } /* Add headers (field names) */ arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); @@ -2420,15 +2724,17 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) show_access_point_info (device, nmc); } } else { - const char *err_msg; if ( nm_device_get_device_type (device) == NM_DEVICE_TYPE_GENERIC - && g_strcmp0 (nm_device_get_type_description (device), "wifi") == 0) - err_msg = _("Error: Device '%s' was not recognized as a Wi-Fi device, check NetworkManager Wi-Fi plugin."); - else - err_msg = _("Error: Device '%s' is not a Wi-Fi device."); - g_string_printf (nmc->return_text, err_msg, ifname); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; + && g_strcmp0 (nm_device_get_type_description (device), "wifi") == 0) { + g_string_printf (nmc->return_text, + _("Error: Device '%s' was not recognized as a Wi-Fi device, check NetworkManager Wi-Fi plugin."), + ifname); + } else { + g_string_printf (nmc->return_text, + _("Error: Device '%s' is not a Wi-Fi device."), + ifname); + } + return NMC_RESULT_ERROR_UNKNOWN; } } else { gboolean empty_line = FALSE; @@ -2479,8 +2785,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) if (!ap) { g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), bssid_user); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + return NMC_RESULT_ERROR_NOT_FOUND; } } else { for (i = 0; devices[i]; i++) { @@ -2501,101 +2806,9 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) } } -error: - g_free (devices); return nmc->return_value; } -/* - * Find a Wi-Fi device with 'iface' in 'devices' array. If 'iface' is NULL, - * the first Wi-Fi device is returned. 'idx' parameter is updated to the point - * where the function finished so that the function can be called repeatedly - * to get next matching device. - * Returns: found device or NULL - */ -static NMDevice * -find_wifi_device_by_iface (const GPtrArray *devices, const char *iface, int *idx) -{ - NMDevice *device = NULL; - int i; - - for (i = *idx; i < devices->len; i++) { - NMDevice *candidate = g_ptr_array_index (devices, i); - const char *dev_iface = nm_device_get_iface (candidate); - - if (!NM_IS_DEVICE_WIFI (candidate)) - continue; - - if (iface) { - /* If a iface was specified then use it. */ - if (g_strcmp0 (dev_iface, iface) == 0) { - device = candidate; - break; - } - } else { - /* Else return the first Wi-Fi device. */ - device = candidate; - break; - } - } - - *idx = i + 1; - return device; -} - -/* - * Find AP on 'device' according to 'bssid' or 'ssid' parameter. - * Returns: found AP or NULL - */ -static NMAccessPoint * -find_ap_on_device (NMDevice *device, GByteArray *bssid, const char *ssid) -{ - const GPtrArray *aps; - NMAccessPoint *ap = NULL; - int i; - - g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL); - g_return_val_if_fail ((bssid && !ssid) || (!bssid && ssid), NULL); - - aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device)); - for (i = 0; i < aps->len; i++) { - NMAccessPoint *candidate_ap = g_ptr_array_index (aps, i); - - if (ssid) { - /* Parameter is SSID */ - GBytes *candidate_ssid; - - candidate_ssid = nm_access_point_get_ssid (candidate_ap); - if (candidate_ssid) { - char *ssid_tmp = nm_utils_ssid_to_utf8 (g_bytes_get_data (candidate_ssid, NULL), - g_bytes_get_size (candidate_ssid)); - - /* Compare SSIDs */ - if (strcmp (ssid, ssid_tmp) == 0) { - ap = candidate_ap; - g_free (ssid_tmp); - break; - } - g_free (ssid_tmp); - } - } else if (bssid) { - /* Parameter is BSSID */ - const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); - char *bssid_up = nm_utils_hwaddr_ntoa (bssid->data, bssid->len); - - /* Compare BSSIDs */ - if (strcmp (bssid_up, candidate_bssid) == 0) { - ap = candidate_ap; - g_free (bssid_up); - break; - } - g_free (bssid_up); - } - } - - return ap; -} - static NMCResultCode do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) { @@ -2619,7 +2832,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) gboolean wep_passphrase = FALSE; GByteArray *bssid1_arr = NULL; GByteArray *bssid2_arr = NULL; - const GPtrArray *devices; + gs_free NMDevice **devices = NULL; int devices_idx; char *ssid_ask = NULL; char *passwd_ask = NULL; @@ -2628,14 +2841,22 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) if (nmc->timeout == -1) nmc->timeout = 90; + devices = nmc_get_devices_sorted (nmc->client); + /* Get the first compulsory argument (SSID or BSSID) */ if (argc > 0) { param_user = *argv; bssid1_arr = nm_utils_hwaddr_atoba (param_user, ETH_ALEN); + if (argc == 1 && nmc->complete) + complete_aps (devices, NULL, param_user, param_user); + argc--; argv++; } else { + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + if (nmc->ask) { ssid_ask = nmc_readline (_("SSID or BSSID: ")); param_user = ssid_ask ? ssid_ask : ""; @@ -2644,46 +2865,56 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) if (!ssid_ask) { g_string_printf (nmc->return_text, _("Error: SSID or BSSID are missing.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } } /* Get the rest of the parameters */ while (argc > 0) { + if (argc == 1 && nmc->complete) { + nmc_complete_strings (*argv, "ifname", "bssid", "password", "wep-key-type", + "name", "private", "hidden", NULL); + } + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } ifname = *argv; + complete_device (devices, ifname, TRUE); } else if (strcmp (*argv, "bssid") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } bssid = *argv; + if (argc == 1 && nmc->complete) + complete_aps (devices, NULL, bssid, NULL); bssid2_arr = nm_utils_hwaddr_atoba (bssid, ETH_ALEN); if (!bssid2_arr) { g_string_printf (nmc->return_text, _("Error: bssid argument value '%s' is not a valid BSSID."), bssid); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } } else if (strcmp (*argv, "password") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } password = *argv; } else if (strcmp (*argv, "wep-key-type") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "key", "phrase", NULL); if (strcmp (*argv, "key") == 0) wep_passphrase = FALSE; else if (strcmp (*argv, "phrase") == 0) @@ -2693,13 +2924,13 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) _("Error: wep-key-type argument value '%s' is invalid, use 'key' or 'phrase'."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } } else if (strcmp (*argv, "name") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } con_name = *argv; } else if (strcmp (*argv, "private") == 0) { @@ -2707,28 +2938,32 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } + if (argc == 1 && nmc->complete) + nmc_complete_bool (*argv); if (!nmc_string_to_bool (*argv, &private, &err_tmp)) { g_string_printf (nmc->return_text, _("Error: %s: %s."), *(argv-1), err_tmp->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; g_clear_error (&err_tmp); - goto error; + goto finish; } } else if (strcmp (*argv, "hidden") == 0) { GError *err_tmp = NULL; if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } + if (argc == 1 && nmc->complete) + nmc_complete_bool (*argv); if (!nmc_string_to_bool (*argv, &hidden, &err_tmp)) { g_string_printf (nmc->return_text, _("Error: %s: %s."), *(argv-1), err_tmp->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; g_clear_error (&err_tmp); - goto error; + goto finish; } - } else { + } else if (!nmc->complete) { g_printerr (_("Unknown parameter: %s\n"), *argv); } @@ -2736,21 +2971,22 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) argv++; } + if (nmc->complete) + goto finish; + /* Verify SSID/BSSID parameters */ if (bssid1_arr && bssid2_arr && memcmp (bssid1_arr->data, bssid2_arr->data, ETH_ALEN)) { g_string_printf (nmc->return_text, _("Error: BSSID to connect to (%s) differs from bssid argument (%s)."), param_user, bssid); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } if (!bssid1_arr && strlen (param_user) > 32) { g_string_printf (nmc->return_text, _("Error: Parameter '%s' is neither SSID nor BSSID."), param_user); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } - devices = nm_client_get_devices (nmc->client); - /* Find a device to activate the connection on */ devices_idx = 0; device = find_wifi_device_by_iface (devices, ifname, &devices_idx); @@ -2761,7 +2997,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) else g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found.")); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; + goto finish; } /* For hidden SSID first scan it so that NM learns about the AP */ @@ -2783,18 +3019,20 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) scan_err->message); g_clear_error (&scan_err); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + goto finish; } } /* Find an AP to connect to */ - ap = find_ap_on_device (device, bssid1_arr, bssid1_arr ? NULL : param_user); + ap = find_ap_on_device (device, bssid1_arr ? param_user : NULL, + bssid1_arr ? NULL : param_user, FALSE); if (!ap && !ifname) { NMDevice *dev; /* AP not found, ifname was not specified, so try finding the AP on another device. */ while ((dev = find_wifi_device_by_iface (devices, NULL, &devices_idx)) != NULL) { - ap = find_ap_on_device (dev, bssid1_arr, bssid1_arr ? NULL : param_user); + ap = find_ap_on_device (dev, bssid1_arr ? param_user : NULL, + bssid1_arr ? NULL : param_user, FALSE); if (ap) { device = dev; break; @@ -2808,7 +3046,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) else g_string_printf (nmc->return_text, _("Error: No access point with BSSID '%s' found."), param_user); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + goto finish; } /* If there are some connection data from user, create a connection and @@ -2911,7 +3149,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) add_and_activate_cb, info); -error: +finish: if (bssid1_arr) g_byte_array_free (bssid1_arr, TRUE); if (bssid2_arr) @@ -3064,8 +3302,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) const char *password = NULL; gboolean show_password = FALSE; NMDevice *device = NULL; - int devices_idx; - const GPtrArray *devices; + gs_free NMDevice **devices = NULL; NMDeviceWifiCapabilities caps; NMConnection *connection = NULL; NMSettingConnection *s_con; @@ -3079,58 +3316,61 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) if (nmc->timeout == -1) nmc->timeout = 60; + devices = nmc_get_devices_sorted (nmc->client); + while (argc > 0) { + if (argc == 1 && nmc->complete) { + nmc_complete_strings (*argv, "ifname", "con-name", "ssid", "band", + "channel", "password", NULL); + } + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } ifname = *argv; + if (argc == 1 && nmc->complete) + complete_device (devices, ifname, TRUE); } else if (strcmp (*argv, "con-name") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } con_name = *argv; } else if (strcmp (*argv, "ssid") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } ssid = *argv; if (strlen (ssid) > 32) { g_string_printf (nmc->return_text, _("Error: ssid is too long.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } } else if (strcmp (*argv, "band") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } band = *argv; + if (argc == 1 && nmc->complete) + nmc_complete_strings (band, "a", "bg", NULL); if (strcmp (band, "a") && strcmp (band, "bg")) { g_string_printf (nmc->return_text, _("Error: band argument value '%s' is invalid; use 'a' or 'bg'."), band); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } } else if (strcmp (*argv, "channel") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } channel = *argv; } else if (strcmp (*argv, "password") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } password = *argv; /* --show-password is deprecated in favour of global --show-secrets option */ @@ -3139,8 +3379,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) show_password = TRUE; } else { g_string_printf (nmc->return_text, _("Error: Unknown parameter %s."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } argc--; @@ -3148,6 +3387,9 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) } show_password = nmc->show_secrets || show_password; + if (nmc->complete) + return nmc->return_value; + /* Verify band and channel parameters */ if (!channel) { if (g_strcmp0 (band, "bg") == 0) @@ -3158,30 +3400,25 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) if (channel) { if (!band) { g_string_printf (nmc->return_text, _("Error: channel requires band too.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } if ( !nmc_string_to_uint (channel, TRUE, 1, 5825, &channel_int) || !nm_utils_wifi_is_channel_valid (channel_int, band)) { g_string_printf (nmc->return_text, _("Error: channel '%s' not valid for band '%s'."), channel, band); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } } /* Find Wi-Fi device. When no ifname is provided, the first Wi-Fi is used. */ - devices = nm_client_get_devices (nmc->client); - devices_idx = 0; - device = find_wifi_device_by_iface (devices, ifname, &devices_idx); + device = find_wifi_device_by_iface (devices, ifname, NULL); if (!device) { if (ifname) g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), ifname); else g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found.")); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; + return NMC_RESULT_ERROR_UNKNOWN; } /* Check device supported mode */ @@ -3193,8 +3430,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) else { g_string_printf (nmc->return_text, _("Error: Device '%s' supports neither AP nor Ad-Hoc mode."), nm_device_get_iface (device)); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; + return NMC_RESULT_ERROR_UNKNOWN; } /* Create a connection with appropriate parameters */ @@ -3227,9 +3463,8 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) if (!set_wireless_security_for_hotspot (s_wsec, wifi_mode, caps, password, show_password, &error)) { g_object_unref (connection); g_string_printf (nmc->return_text, _("Error: Invalid 'password': %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; g_clear_error (&error); - goto error; + return NMC_RESULT_ERROR_UNKNOWN; } s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); @@ -3242,7 +3477,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) /* Activate the connection now */ nmc->nowait_flag = (nmc->timeout == 0); - nmc->should_wait = TRUE; + nmc->should_wait++; info = g_malloc0 (sizeof (AddAndActivateInfo)); info->nmc = nmc; @@ -3257,7 +3492,6 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) add_and_activate_cb, info); -error: return nmc->return_value; } @@ -3282,47 +3516,53 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv) NMDevice *device; const char *ifname = NULL; GPtrArray *ssids; - const GPtrArray *devices; - int devices_idx; + gs_free NMDevice **devices = NULL; GVariantBuilder builder, array_builder; GVariant *options; const char *ssid; int i; ssids = g_ptr_array_new (); + devices = nmc_get_devices_sorted (nmc->client); /* Get the parameters */ while (argc > 0) { + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "ifname", "ssid", NULL); + if (strcmp (*argv, "ifname") == 0) { if (ifname) { g_string_printf (nmc->return_text, _("Error: '%s' cannot repeat."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } ifname = *argv; + if (argc == 1 && nmc->complete) + complete_device (devices, ifname, TRUE); } else if (strcmp (*argv, "ssid") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } g_ptr_array_add (ssids, *argv); - } else + } else if (!nmc->complete) g_printerr (_("Unknown parameter: %s\n"), *argv); argc--; argv++; } + if (nmc->complete) + goto finish; + /* Find Wi-Fi device to scan on. When no ifname is provided, the first Wi-Fi is used. */ - devices = nm_client_get_devices (nmc->client); - devices_idx = 0; - device = find_wifi_device_by_iface (devices, ifname, &devices_idx); + device = find_wifi_device_by_iface (devices, ifname, NULL); if (!device) { if (ifname) @@ -3330,7 +3570,7 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv) else g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found.")); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; + goto finish; } @@ -3353,35 +3593,32 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv) nm_device_wifi_request_scan_async (NM_DEVICE_WIFI (device), NULL, request_rescan_cb, nmc); - g_ptr_array_free (ssids, FALSE); - return nmc->return_value; -error: nmc->should_wait++; +finish: g_ptr_array_free (ssids, FALSE); return nmc->return_value; } +static NMCCommand device_wifi_cmds[] = { + {"list", do_device_wifi_list, NULL }, + {"connect", do_device_wifi_connect_network, NULL }, + {"hotspot", do_device_wifi_hotspot, NULL }, + {"rescan", do_device_wifi_rescan, NULL }, + {NULL, do_device_wifi_list, NULL } +}; + static NMCResultCode do_device_wifi (NmCli *nmc, int argc, char **argv) { - if (argc == 0) - nmc->return_value = do_device_wifi_list (nmc, argc-1, argv+1); - else if (argc > 0) { - if (matches (*argv, "list") == 0) { - nmc->return_value = do_device_wifi_list (nmc, argc-1, argv+1); - } else if (matches (*argv, "connect") == 0) { - nmc->return_value = do_device_wifi_connect_network (nmc, argc-1, argv+1); - } else if (matches (*argv, "hotspot") == 0) { - nmc->return_value = do_device_wifi_hotspot (nmc, argc-1, argv+1); - } else if (matches (*argv, "rescan") == 0) { - nmc->return_value = do_device_wifi_rescan (nmc, argc-1, argv+1); - } else { - g_string_printf (nmc->return_text, _("Error: 'device wifi' command '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } + GError *error = NULL; + + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + g_error_free (error); + return NMC_RESULT_ERROR_USER_INPUT; } - return nmc->return_value; + return nmc_do_cmd (nmc, device_wifi_cmds, *argv, argc, argv); } static int @@ -3468,27 +3705,32 @@ show_device_lldp_list (NMDevice *device, NmCli *nmc, char *fields_str, int *coun return neighbors->len; } -static gboolean +static NMCResultCode do_device_lldp_list (NmCli *nmc, int argc, char **argv) { - NMDevice *device = NULL, **devices = NULL; - GError *error = NULL; - const char *ifname = NULL; + NMDevice *device = NULL; + gs_free_error GError *error = NULL; char *fields_str; - int i, counter = 0; + int counter = 0; while (argc > 0) { + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "ifname", NULL); + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; + } + + device = get_device (nmc, &argc, &argv, &error); + if (!device) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return error->code; } - ifname = *argv; } else { g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } argc--; @@ -3506,57 +3748,50 @@ do_device_lldp_list (NmCli *nmc, int argc, char **argv) if (error) { g_string_printf (nmc->return_text, _("Error: 'device lldp list': %s"), error->message); - g_error_free (error); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; + return NMC_RESULT_ERROR_USER_INPUT; } - devices = get_devices_sorted (nmc->client); - - if (ifname) { - for (i = 0; devices[i]; i++) { - NMDevice *candidate = devices[i]; - const char *dev_iface = nm_device_get_iface (candidate); - - if (!g_strcmp0 (dev_iface, ifname)) { - device = candidate; - break; - } - } - - if (!device) { - g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; - } + if (nmc->complete) + return nmc->return_value; + if (device) { nmc_empty_output_fields (nmc); show_device_lldp_list (device, nmc, fields_str, &counter); } else { + NMDevice **devices = nmc_get_devices_sorted (nmc->client); + int i; + for (i = 0; devices[i]; i++) { nmc_empty_output_fields (nmc); show_device_lldp_list (devices[i], nmc, fields_str, &counter); } + + g_free (devices); } -error: - g_free (devices); return nmc->return_value; } +static NMCCommand device_lldp_cmds[] = { + {"list", do_device_lldp_list, NULL }, + {NULL, do_device_lldp_list, NULL } +}; + static NMCResultCode do_device_lldp (NmCli *nmc, int argc, char **argv) { - if (argc == 0) - nmc->return_value = do_device_lldp_list (nmc, argc, argv); - else if (matches (*argv, "list") == 0) - nmc->return_value = do_device_lldp_list (nmc, argc-1, argv+1); - else { - g_string_printf (nmc->return_text, _("Error: 'device lldp' command '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + GError *error = NULL; + + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + g_error_free (error); + return NMC_RESULT_ERROR_USER_INPUT; } - return nmc->return_value; + if (!nmc->mode_specified) + nmc->multiline_output = TRUE; /* multiline mode is default for 'device lldp' */ + + return nmc_do_cmd (nmc, device_lldp_cmds, *argv, argc, argv); } static gboolean @@ -3628,11 +3863,24 @@ nmcli_device_tab_completion (const char *text, int start, int end) return match_array; } +static const NMCCommand device_cmds[] = { + {"status", do_devices_status, usage_device_status }, + {"show", do_device_show, usage_device_show }, + {"connect", do_device_connect, usage_device_connect }, + {"reapply", do_device_reapply, usage_device_reapply }, + {"disconnect", do_devices_disconnect, usage_device_disconnect }, + {"delete", do_devices_delete, usage_device_delete }, + {"set", do_device_set, usage_device_set }, + {"monitor", do_devices_monitor, usage_device_monitor }, + {"wifi", do_device_wifi, usage_device_wifi }, + {"lldp", do_device_lldp, usage_device_lldp }, + {"modify", do_device_modify, usage_device_modify }, + {NULL, do_devices_status, usage }, +}; + NMCResultCode do_devices (NmCli *nmc, int argc, char **argv) { - GError *error = NULL; - /* Register polkit agent */ nmc_start_polkit_agent_start_try (nmc); @@ -3644,120 +3892,14 @@ do_devices (NmCli *nmc, int argc, char **argv) /* Check whether NetworkManager is running */ if (!nm_client_get_nm_running (nmc->client)) { g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - return nmc->return_value; + return NMC_RESULT_ERROR_NM_NOT_RUNNING; } - if (argc == 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - nmc->return_value = do_devices_status (nmc, 0, NULL); - } - - if (argc > 0) { - if (nmc_arg_is_help (*argv)) { - usage (); - goto usage_exit; - } - else if (matches (*argv, "status") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_status (); - goto usage_exit; - } - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - nmc->return_value = do_devices_status (nmc, argc-1, argv+1); - } - else if (matches (*argv, "show") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_show (); - goto usage_exit; - } - if (!nmc->mode_specified) - nmc->multiline_output = TRUE; /* multiline mode is default for 'device show' */ - nmc->return_value = do_devices_show (nmc, argc-1, argv+1); - } - else if (matches (*argv, "connect") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_connect (); - goto usage_exit; - } - nmc->return_value = do_device_connect (nmc, argc-1, argv+1); - } - else if (matches (*argv, "reapply") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_reapply (); - goto usage_exit; - } - nmc->return_value = do_device_reapply (nmc, argc-1, argv+1); - } - else if (matches (*argv, "disconnect") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_disconnect (); - goto usage_exit; - } - nmc->return_value = do_device_disconnect (nmc, argc-1, argv+1); - } - else if (matches (*argv, "delete") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_delete (); - goto usage_exit; - } - nmc->return_value = do_device_delete (nmc, argc-1, argv+1); - } - else if (matches (*argv, "set") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_set (); - goto usage_exit; - } - nmc->return_value = do_device_set (nmc, argc-1, argv+1); - } - else if (matches (*argv, "monitor") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_monitor (); - goto usage_exit; - } - nmc->return_value = do_device_monitor (nmc, argc-1, argv+1); - } - else if (matches (*argv, "wifi") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_wifi (); - goto usage_exit; - } - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - nmc->return_value = do_device_wifi (nmc, argc-1, argv+1); - } - else if (matches (*argv, "lldp") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_lldp (); - goto usage_exit; - } - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - if (!nmc->mode_specified) - nmc->multiline_output = TRUE; /* multiline mode is default for 'device lldp' */ - nmc->return_value = do_device_lldp (nmc, argc-1, argv+1); - } - else { - usage (); - g_string_printf (nmc->return_text, _("Error: 'dev' command '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } - } - -usage_exit: - return nmc->return_value; - -opt_error: - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - g_error_free (error); - return nmc->return_value; + return nmc_do_cmd (nmc, device_cmds, *argv, argc, argv); } void monitor_devices (NmCli *nmc) { - do_device_monitor (nmc, 0, NULL); + do_devices_monitor (nmc, 0, NULL); } diff --git a/clients/cli/devices.h b/clients/cli/devices.h index 56fc9c2f..672944be 100644 --- a/clients/cli/devices.h +++ b/clients/cli/devices.h @@ -24,6 +24,30 @@ NMCResultCode do_devices (NmCli *nmc, int argc, char **argv); +void nmc_complete_device (NMClient *client, const char *prefix, gboolean wifi_only); + +void nmc_complete_bssid (NMClient *client, const char *ifname, const char *bssid_prefix); + void monitor_devices (NmCli *nmc); +NMDevice ** nmc_get_devices_sorted (NMClient *client); + +void nmc_device_state_to_color (NMDeviceState state, NmcTermColor *color, NmcTermFormat *color_fmt); + +extern NmcOutputField nmc_fields_dev_status[]; +extern NmcOutputField nmc_fields_dev_show_general[]; +extern NmcOutputField nmc_fields_dev_show_connections[]; +extern NmcOutputField nmc_fields_dev_show_cap[]; +extern NmcOutputField nmc_fields_dev_show_wired_prop[]; +extern NmcOutputField nmc_fields_dev_show_wifi_prop[]; +extern NmcOutputField nmc_fields_dev_show_wimax_prop[]; +extern NmcOutputField nmc_fields_dev_wifi_list[]; +extern NmcOutputField nmc_fields_dev_wimax_list[]; +extern NmcOutputField nmc_fields_dev_show_master_prop[]; +extern NmcOutputField nmc_fields_dev_show_team_prop[]; +extern NmcOutputField nmc_fields_dev_show_vlan_prop[]; +extern NmcOutputField nmc_fields_dev_show_bluetooth[]; +extern NmcOutputField nmc_fields_dev_show_sections[]; +extern NmcOutputField nmc_fields_dev_lldp_list[]; + #endif /* NMC_DEVICES_H */ diff --git a/clients/cli/general.c b/clients/cli/general.c index 280630c1..e9709125 100644 --- a/clients/cli/general.c +++ b/clients/cli/general.c @@ -24,7 +24,9 @@ #include "polkit-agent.h" #include "utils.h" +#include "common.h" #include "general.h" +#include "common.h" #include "nm-common-macros.h" #include "devices.h" @@ -388,6 +390,23 @@ show_nm_status (NmCli *nmc, const char *pretty_header_name, const char *print_fl return TRUE; } +static NMCResultCode +do_general_status (NmCli *nmc, int argc, char **argv) +{ + gs_free_error GError *error = NULL; + + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return NMC_RESULT_ERROR_USER_INPUT; + } + + if (nmc->complete) + return nmc->return_value; + + show_nm_status (nmc, NULL, NULL); + return nmc->return_value; +} + static const char * permission_to_string (NMClientPermission perm) { @@ -418,6 +437,10 @@ permission_to_string (NMClientPermission perm) return NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS; case NM_CLIENT_PERMISSION_RELOAD: return NM_AUTH_PERMISSION_RELOAD; + case NM_CLIENT_PERMISSION_CHECKPOINT_ROLLBACK: + return NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK; + case NM_CLIENT_PERMISSION_ENABLE_DISABLE_STATISTICS: + return NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS; default: return _("unknown"); } @@ -492,6 +515,23 @@ show_nm_permissions (NmCli *nmc) return TRUE; } +static NMCResultCode +do_general_permissions (NmCli *nmc, int argc, char **argv) +{ + gs_free_error GError *error = NULL; + + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return NMC_RESULT_ERROR_USER_INPUT; + } + + if (nmc->complete) + return nmc->return_value; + + show_nm_permissions (nmc); + return nmc->return_value; +} + static gboolean show_general_logging (NmCli *nmc) { @@ -545,6 +585,51 @@ show_general_logging (NmCli *nmc) return TRUE; } +static NMCResultCode +do_general_logging (NmCli *nmc, int argc, char **argv) +{ + gs_free_error GError *error = NULL; + + if (argc == 0) { + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + g_error_free (error); + return NMC_RESULT_ERROR_USER_INPUT; + } + + if (nmc->complete) + return nmc->return_value; + + show_general_logging (nmc); + } else { + /* arguments provided -> set logging level and domains */ + const char *level = NULL; + const char *domains = NULL; + nmc_arg_t exp_args[] = { {"level", TRUE, &level, TRUE}, + {"domains", TRUE, &domains, TRUE}, + {NULL} }; + + /* TODO: nmc_parse_args needs completion */ + if (nmc->complete) + return nmc->return_value; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) { + g_string_assign (nmc->return_text, error->message); + return error->code; + } + + nmc->get_client (nmc); /* create NMClient */ + nm_client_set_logging (nmc->client, level, domains, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: failed to set logging: %s"), + nmc_error_get_simple_message (error)); + return NMC_RESULT_ERROR_UNKNOWN; + } + } + + return nmc->return_value; +} + static void save_hostname_cb (GObject *object, GAsyncResult *result, gpointer user_data) { @@ -561,129 +646,55 @@ save_hostname_cb (GObject *object, GAsyncResult *result, gpointer user_data) quit (); } -/* - * Entry point function for general operations 'nmcli general' - */ -NMCResultCode -do_general (NmCli *nmc, int argc, char **argv) +static NMCResultCode +do_general_hostname (NmCli *nmc, int argc, char **argv) { - GError *error = NULL; - - /* Register polkit agent */ - nmc_start_polkit_agent_start_try (nmc); + if (nmc->complete) + return nmc->return_value; if (argc == 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - show_nm_status (nmc, NULL, NULL); - } + /* no arguments -> get hostname */ + char *hostname = NULL; + + nmc->get_client (nmc); /* create NMClient */ + g_object_get (nmc->client, NM_CLIENT_HOSTNAME, &hostname, NULL); + if (hostname) + g_print ("%s\n", hostname); + g_free (hostname); + } else { + /* hostname provided -> set it */ + const char *hostname = *argv; - if (argc > 0) { - if (nmc_arg_is_help (*argv)) { - usage_general (); - } - else if (matches (*argv, "status") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_general_status (); - goto finish; - } - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - show_nm_status (nmc, NULL, NULL); - } - else if (matches (*argv, "hostname") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_general_hostname (); - goto finish; - } + if (next_arg (&argc, &argv) == 0) + g_print ("Warning: ignoring extra garbage after '%s' hostname\n", hostname); - if (next_arg (&argc, &argv) != 0) { - /* no arguments -> get hostname */ - char *hostname = NULL; + nmc->should_wait++; + nmc->get_client (nmc); /* create NMClient */ + nm_client_save_hostname_async (nmc->client, hostname, NULL, save_hostname_cb, nmc); + } - nmc->get_client (nmc); /* create NMClient */ - g_object_get (nmc->client, NM_CLIENT_HOSTNAME, &hostname, NULL); - if (hostname) - g_print ("%s\n", hostname); - g_free (hostname); - } else { - /* hostname provided -> set it */ - const char *hostname = *argv; + return nmc->return_value; - if (next_arg (&argc, &argv) == 0) - g_print ("Warning: ignoring extra garbage after '%s' hostname\n", hostname); +} - nmc->should_wait++; - nmc->get_client (nmc); /* create NMClient */ - nm_client_save_hostname_async (nmc->client, hostname, NULL, save_hostname_cb, nmc); - } - } - else if (matches (*argv, "permissions") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_general_permissions (); - goto finish; - } - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - show_nm_permissions (nmc); - } - else if (matches (*argv, "logging") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_general_logging (); - goto finish; - } - if (next_arg (&argc, &argv) != 0) { - /* no arguments -> get logging level and domains */ - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - show_general_logging (nmc); - } else { - /* arguments provided -> set logging level and domains */ - const char *level = NULL; - const char *domains = NULL; - nmc_arg_t exp_args[] = { {"level", TRUE, &level, TRUE}, - {"domains", TRUE, &domains, TRUE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) { - g_string_assign (nmc->return_text, error->message); - nmc->return_value = error->code; - goto finish; - } +static const NMCCommand general_cmds[] = { + {"status", do_general_status, usage_general_status }, + {"hostname", do_general_hostname, usage_general_hostname }, + {"permissions", do_general_permissions, usage_general_permissions }, + {"logging", do_general_logging, usage_general_logging }, + {NULL, do_general_status, usage_general } +}; - nmc->get_client (nmc); /* create NMClient */ - nm_client_set_logging (nmc->client, level, domains, &error); - if (error) { - g_string_printf (nmc->return_text, _("Error: failed to set logging: %s"), - error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto finish; - } - } - } - else { - usage_general (); - g_string_printf (nmc->return_text, _("Error: 'general' command '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } - } +/* + * Entry point function for general operations 'nmcli general' + */ +NMCResultCode +do_general (NmCli *nmc, int argc, char **argv) +{ + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); -finish: - if (error) - g_error_free (error); - return nmc->return_value; + return nmc_do_cmd (nmc, general_cmds, *argv, argc, argv); } static gboolean @@ -743,12 +754,28 @@ do_networking (NmCli *nmc, int argc, char **argv) /* Register polkit agent */ nmc_start_polkit_agent_start_try (nmc); - if (argc == 0) + if (argc == 0) { + if (nmc->complete) + return nmc->return_value; nmc_switch_show (nmc, NMC_FIELDS_NM_NETWORKING, _("Networking")); - else if (argc > 0) { + } else if (argc > 0) { + + if (argc == 1 && nmc->complete) { + nmc_complete_strings (*argv, "connectivity", NULL); + nmc_complete_bool (*argv); + return nmc->return_value; + } + if (nmc_arg_is_help (*argv)) { + if (nmc->complete) + return nmc->return_value; usage_networking (); } else if (matches (*argv, "connectivity") == 0) { + if (nmc->complete) { + if (argc == 2) + nmc_complete_strings (*(argv+1), "check", NULL); + return nmc->return_value; + } if (nmc_arg_is_help (*(argv+1))) { usage_networking_connectivity (); goto finish; @@ -773,6 +800,8 @@ do_networking (NmCli *nmc, int argc, char **argv) nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; } } else if (nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) { + if (nmc->complete) + return nmc->return_value; if (nmc_arg_is_help (*(argv+1))) { if (enable_flag) usage_networking_on (); @@ -784,6 +813,8 @@ do_networking (NmCli *nmc, int argc, char **argv) nmc->get_client (nmc); /* create NMClient */ nm_client_networking_set_enabled (nmc->client, enable_flag, NULL); } else { + if (nmc->complete) + return nmc->return_value; usage_networking (); g_string_printf (nmc->return_text, _("Error: 'networking' command '%s' is not valid."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; @@ -795,118 +826,125 @@ finish: return nmc->return_value; } -/* - * Entry point function for radio switch commands 'nmcli radio' - */ -NMCResultCode -do_radio (NmCli *nmc, int argc, char **argv) +static NMCResultCode +do_radio_all (NmCli *nmc, int argc, char **argv) { - GError *error = NULL; gboolean enable_flag; - - /* Register polkit agent */ - nmc_start_polkit_agent_start_try (nmc); + gs_free_error GError *error = NULL; if (argc == 0) { + if (nmc->complete) + return nmc->return_value; + + /* no argument, show all radio switches */ if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - g_error_free (error); - goto finish; + return NMC_RESULT_ERROR_USER_INPUT; } show_nm_status (nmc, _("Radio switches"), NMC_FIELDS_NM_STATUS_RADIO); + } else { + if (nmc->complete) { + if (argc == 1) + nmc_complete_bool (*argv); + return nmc->return_value; + } + + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + return nmc->return_value; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_wireless_set_enabled (nmc->client, enable_flag); + nm_client_wimax_set_enabled (nmc->client, enable_flag); + nm_client_wwan_set_enabled (nmc->client, enable_flag); } - if (argc > 0) { - if (nmc_arg_is_help (*argv)) { - usage_radio (); - } - else if (matches (*argv, "all") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_radio_all (); - goto finish; - } - if (next_arg (&argc, &argv) != 0) { - /* no argument, show all radio switches */ - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - g_error_free (error); - goto finish; - } - show_nm_status (nmc, _("Radio switches"), NMC_FIELDS_NM_STATUS_RADIO); - } else { - if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) - goto finish; + return nmc->return_value; +} - nmc->get_client (nmc); /* create NMClient */ - nm_client_wireless_set_enabled (nmc->client, enable_flag); - nm_client_wimax_set_enabled (nmc->client, enable_flag); - nm_client_wwan_set_enabled (nmc->client, enable_flag); - } - } - else if (matches (*argv, "wifi") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_radio_wifi (); - goto finish; - } - if (next_arg (&argc, &argv) != 0) { - /* no argument, show current WiFi state */ - nmc_switch_show (nmc, NMC_FIELDS_NM_WIFI, _("Wi-Fi radio switch")); - } else { - if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) - goto finish; - - nmc->get_client (nmc); /* create NMClient */ - nm_client_wireless_set_enabled (nmc->client, enable_flag); - } - } - else if (matches (*argv, "wwan") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_radio_wwan (); - goto finish; - } - if (next_arg (&argc, &argv) != 0) { - /* no argument, show current WWAN (mobile broadband) state */ - nmc_switch_show (nmc, NMC_FIELDS_NM_WWAN, _("WWAN radio switch")); - } else { - if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) - goto finish; +static NMCResultCode +do_radio_wifi (NmCli *nmc, int argc, char **argv) +{ + gboolean enable_flag; - nmc->get_client (nmc); /* create NMClient */ - nm_client_wwan_set_enabled (nmc->client, enable_flag); - } + if (argc == 0) { + if (nmc->complete) + return nmc->return_value; + + /* no argument, show current WiFi state */ + nmc_switch_show (nmc, NMC_FIELDS_NM_WIFI, _("Wi-Fi radio switch")); + } else { + if (nmc->complete) { + if (argc == 1) + nmc_complete_bool (*argv); + return nmc->return_value; } - else { - usage_radio (); - g_string_printf (nmc->return_text, _("Error: 'radio' command '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + return nmc->return_value; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_wireless_set_enabled (nmc->client, enable_flag); + } + + return nmc->return_value; +} + +static NMCResultCode +do_radio_wwan (NmCli *nmc, int argc, char **argv) +{ + gboolean enable_flag; + + if (argc == 0) { + if (nmc->complete) + return nmc->return_value; + + /* no argument, show current WWAN (mobile broadband) state */ + nmc_switch_show (nmc, NMC_FIELDS_NM_WWAN, _("WWAN radio switch")); + } else { + if (nmc->complete) { + if (argc == 1) + nmc_complete_bool (*argv); + return nmc->return_value; } + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + return nmc->return_value; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_wwan_set_enabled (nmc->client, enable_flag); } -finish: - quit (); return nmc->return_value; } +static const NMCCommand radio_cmds[] = { + {"all", do_radio_all, usage_radio_all }, + {"wifi", do_radio_wifi, usage_radio_wifi }, + {"wwan", do_radio_wwan, usage_radio_wwan }, + {NULL, do_radio_all, usage_radio } +}; + +/* + * Entry point function for radio switch commands 'nmcli radio' + */ +NMCResultCode +do_radio (NmCli *nmc, int argc, char **argv) +{ + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + + return nmc_do_cmd (nmc, radio_cmds, *argv, argc, argv); +} + static void networkmanager_running (NMClient *client, GParamSpec *param, NmCli *nmc) { gboolean running; - NmcTermColor color; - const char *message; char *str; running = nm_client_get_nm_running (client); - if (running) { - color = NMC_TERM_COLOR_GREEN; - message = _("NetworkManager has started"); - } else { - color = NMC_TERM_COLOR_RED; - message = _("NetworkManager has stopped"); - } - - str = nmc_colorize (nmc, color, NMC_TERM_FORMAT_NORMAL, message); + str = nmc_colorize (nmc, + running ? NMC_TERM_COLOR_GREEN : NMC_TERM_COLOR_RED, + NMC_TERM_FORMAT_NORMAL, + running ? _("NetworkManager has started") : _("NetworkManager has stopped")); g_print ("%s\n", str); g_free (str); } @@ -965,9 +1003,231 @@ client_state (NMClient *client, GParamSpec *param, NmCli *nmc) g_free (str); } + +static void +device_overview (NmCli *nmc, NMDevice *device) +{ + GString *outbuf = g_string_sized_new (80); + char *tmp; + const GPtrArray *activatable; + + activatable = nm_device_get_available_connections (device); + + g_string_append_printf (outbuf, "%s", nm_device_get_type_description (device)); + + if (nm_device_get_state (device) == NM_DEVICE_STATE_DISCONNECTED) { + if (activatable) { + if (activatable->len == 1) + g_print ("\t%d %s\n", activatable->len, _("connection available")); + else if (activatable->len > 1) + g_print ("\t%d %s\n", activatable->len, _("connections available")); + } + } + + if ( nm_device_get_driver (device) + && strcmp (nm_device_get_driver (device), "") + && strcmp (nm_device_get_driver (device), nm_device_get_type_description (device))) { + g_string_append_printf (outbuf, " (%s)", nm_device_get_driver (device)); + } + + g_string_append_printf (outbuf, ", "); + + if ( nm_device_get_hw_address (device) + && strcmp (nm_device_get_hw_address (device), "")) { + g_string_append_printf (outbuf, "%s, ", nm_device_get_hw_address (device)); + } + + if (!nm_device_get_autoconnect (device)) + g_string_append_printf (outbuf, "%s, ", _("autoconnect")); + if (nm_device_get_firmware_missing (device)) { + tmp = nmc_colorize (nmc, NMC_TERM_COLOR_RED, NMC_TERM_FORMAT_NORMAL, _("fw missing")); + g_string_append_printf (outbuf, "%s, ", tmp); + g_free (tmp); + } + if (nm_device_get_nm_plugin_missing (device)) { + tmp = nmc_colorize (nmc, NMC_TERM_COLOR_RED, NMC_TERM_FORMAT_NORMAL, _("plugin missing")); + g_string_append_printf (outbuf, "%s, ", tmp); + g_free (tmp); + } + if (nm_device_is_software (device)) + g_string_append_printf (outbuf, "%s, ", _("sw")); + else + g_string_append_printf (outbuf, "%s, ", _("hw")); + + if ( nm_device_get_ip_iface (device) + && g_strcmp0 (nm_device_get_ip_iface (device), nm_device_get_iface (device)) + && g_strcmp0 (nm_device_get_ip_iface (device), "")) + g_string_append_printf (outbuf, "%s %s,", _("iface"), nm_device_get_ip_iface (device)); + + if (nm_device_get_physical_port_id (device)) + g_string_append_printf (outbuf, "%s %s, ", _("port"), nm_device_get_physical_port_id (device)); + + if (nm_device_get_mtu (device)) + g_string_append_printf (outbuf, "%s %d, ", _("mtu"), nm_device_get_mtu (device)); + + if (outbuf->len >= 2) { + g_string_truncate (outbuf, outbuf->len - 2); + g_print ("\t%s\n", outbuf->str); + } + + g_string_free (outbuf, TRUE); +} + +static void +ac_overview (NmCli *nmc, NMActiveConnection *ac) +{ + GString *outbuf = g_string_sized_new (80); + NMIPConfig *ip; + + if (nm_active_connection_get_master (ac)) { + g_string_append_printf (outbuf, "%s %s,", _("master"), + nm_device_get_iface (nm_active_connection_get_master (ac))); + } + if (nm_active_connection_get_vpn (ac)) + g_string_append_printf (outbuf, "%s, ", _("VPN")); + if (nm_active_connection_get_default (ac)) + g_string_append_printf (outbuf, "%s, ", _("ip4 default")); + if (nm_active_connection_get_default6 (ac)) + g_string_append_printf (outbuf, "%s, ", _("ip6 default")); + if (outbuf->len >= 2) { + g_string_truncate (outbuf, outbuf->len - 2); + g_print ("\t%s\n", outbuf->str); + } + + ip = nm_active_connection_get_ip4_config (ac); + if (ip) { + const GPtrArray *p; + int i; + + p = nm_ip_config_get_addresses (ip); + for (i = 0; i < p->len; i++) { + NMIPAddress *a = p->pdata[i]; + g_print ("\tinet4 %s/%d\n", nm_ip_address_get_address (a), + nm_ip_address_get_prefix (a)); + } + + p = nm_ip_config_get_routes (ip); + for (i = 0; i < p->len; i++) { + NMIPRoute *a = p->pdata[i]; + g_print ("\troute4 %s/%d\n", nm_ip_route_get_dest (a), + nm_ip_route_get_prefix (a)); + } + } + + ip = nm_active_connection_get_ip6_config (ac); + if (ip) { + const GPtrArray *p; + int i; + + p = nm_ip_config_get_addresses (ip); + for (i = 0; i < p->len; i++) { + NMIPAddress *a = p->pdata[i]; + g_print ("\tinet6 %s/%d\n", nm_ip_address_get_address (a), + nm_ip_address_get_prefix (a)); + } + + p = nm_ip_config_get_routes (ip); + for (i = 0; i < p->len; i++) { + NMIPRoute *a = p->pdata[i]; + g_print ("\troute6 %s/%d\n", nm_ip_route_get_dest (a), + nm_ip_route_get_prefix (a)); + } + } + + g_string_free (outbuf, TRUE); +} + +/* + * Entry point function for 'nmcli' without arguments. + */ +NMCResultCode +do_overview (NmCli *nmc, int argc, char **argv) +{ + NMDevice **devices; + const GPtrArray *p; + NMActiveConnection *ac; + NmcTermColor color; + char *tmp; + int i; + + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + + /* Get NMClient object early */ + nmc->get_client (nmc); + + /* Check whether NetworkManager is running */ + if (!nm_client_get_nm_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + return NMC_RESULT_ERROR_NM_NOT_RUNNING; + } + + /* The VPN connections don't have devices (yet?). */ + p = nm_client_get_active_connections (nmc->client); + for (i = 0; i < p->len; i++) { + NMActiveConnectionState state; + + ac = p->pdata[i]; + + if (!nm_active_connection_get_vpn (ac)) + continue; + + state = nm_active_connection_get_state (ac); + nmc_active_connection_state_to_color (state, &color); + tmp = nmc_colorize (nmc, color, NMC_TERM_FORMAT_NORMAL, _("%s VPN connection"), + nm_active_connection_get_id (ac)); + g_print ("%s\n", tmp); + g_free (tmp); + + ac_overview (nmc, ac); + g_print ("\n"); + } + + devices = nmc_get_devices_sorted (nmc->client); + for (i = 0; devices[i]; i++) { + NmcTermFormat color_fmt; + NMDeviceState state; + + ac = nm_device_get_active_connection (devices[i]); + + state = nm_device_get_state (devices[i]); + nmc_device_state_to_color (state, &color, &color_fmt); + tmp = nmc_colorize (nmc, color, color_fmt, "%s: %s%s%s", + nm_device_get_iface (devices[i]), + nmc_device_state_to_string (state), + ac ? " to " : "", + ac ? nm_active_connection_get_id (ac) : ""); + g_print ("%s\n", tmp); + g_free (tmp); + + if (nm_device_get_description (devices[i]) && strcmp (nm_device_get_description (devices[i]), "")) + g_print ("\t\"%s\"\n", nm_device_get_description (devices[i])); + + + device_overview (nmc, devices[i]); + if (ac) + ac_overview (nmc, ac); + g_print ("\n"); + } + g_free (devices); + + g_print (_("Use \"nmcli device show\" to get complete information about known devices and\n" + "\"nmcli connection show\" to get an overview on active connection profiles.\n" + "\n" + "Consult nmcli(1) and nmcli-examples(5) manual pages for complete usage details.\n")); + + return NMC_RESULT_SUCCESS; +} + +/* + * Entry point function for 'nmcli monitor' + */ NMCResultCode do_monitor (NmCli *nmc, int argc, char **argv) { + if (nmc->complete) + return nmc->return_value; + if (argc > 0) { if (!nmc_arg_is_help (*argv)) { g_string_printf (nmc->return_text, _("Error: 'monitor' command '%s' is not valid."), *argv); diff --git a/clients/cli/general.h b/clients/cli/general.h index 40dddb20..05caffea 100644 --- a/clients/cli/general.h +++ b/clients/cli/general.h @@ -26,5 +26,6 @@ NMCResultCode do_general (NmCli *nmc, int argc, char **argv); NMCResultCode do_networking (NmCli *nmc, int argc, char **argv); NMCResultCode do_radio (NmCli *nmc, int argc, char **argv); NMCResultCode do_monitor (NmCli *nmc, int argc, char **argv); +NMCResultCode do_overview (NmCli *nmc, int argc, char **argv); #endif /* NMC_GENERAL_H */ diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index ffab7d8b..e78ab85c 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -123,6 +123,8 @@ _nmcli_compl_match_option() S="${S:2}" elif [[ "${S:0:1}" == "-" ]]; then S="${S:1}" + else + return 1 fi for V; do case "$V" in @@ -231,14 +233,14 @@ _nmcli_compl_OPTIONS() mode) if [[ "${#words[@]}" -eq 2 ]]; then _nmcli_list "tabular multiline" - return 0 + return 0 fi _nmcli_array_delete_at words 0 1 ;; colors) if [[ "${#words[@]}" -eq 2 ]]; then _nmcli_list "yes no auto" - return 0 + return 0 fi _nmcli_array_delete_at words 0 1 ;; @@ -249,21 +251,21 @@ _nmcli_compl_OPTIONS() connection 802-3-ethernet 802-1x 802-11-wireless 802-11-wireless-security ipv4 ipv6 serial ppp pppoe gsm cdma bluetooth 802-11-olpc-mesh vpn wimax infiniband bond vlan adsl bridge bridge-port team team-port dcb tun ip-tunnel macvlan vxlan GENERAL IP4 DHCP4 IP6 DHCP6 VPN profile active" - return 0 + return 0 fi _nmcli_array_delete_at words 0 1 ;; escape) if [[ "${#words[@]}" -eq 2 ]]; then _nmcli_list "no yes" - return 0 + return 0 fi _nmcli_array_delete_at words 0 1 ;; wait) if [[ "${#words[@]}" -eq 2 ]]; then _nmcli_list "" - return 0 + return 0 fi _nmcli_array_delete_at words 0 1 ;; @@ -328,13 +330,13 @@ _nmcli_compl_ARGS() case "${WORD0}" in level) if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "OFF ERR WARN INFO DEBUG TRACE" + _nmcli_list "OFF ERR WARN INFO DEBUG TRACE KEEP" return 0 fi ;; domains) if [[ "${#words[@]}" -eq 2 ]]; then - local OPTIONS_DOM=(ALL DEFAULT PLATFORM RFKILL ETHER WIFI BT MB DHCP4 DHCP6 PPP WIFI_SCAN IP4 IP6 AUTOIP4 DNS VPN SHARING SUPPLICANT AGENTS SETTINGS SUSPEND CORE DEVICE OLPC WIMAX INFINIBAND FIREWALL ADSL BOND VLAN BRIDGE DBUS_PROPS TEAM CONCHECK DCB DISPATCH) + local OPTIONS_DOM=(ALL DEFAULT DHCP IP PLATFORM RFKILL ETHER WIFI BT MB DHCP4 DHCP6 PPP WIFI_SCAN IP4 IP6 AUTOIP4 DNS VPN SHARING SUPPLICANT AGENTS SETTINGS SUSPEND CORE DEVICE OLPC WIMAX INFINIBAND FIREWALL ADSL BOND VLAN BRIDGE DBUS_PROPS TEAM CONCHECK DCB DISPATCH AUDIT SYSTEMD VPN_PLUGIN) if [[ "${words[1]}" != "" ]]; then # split the comma separaeted domain string into its parts LOGD @@ -761,21 +763,14 @@ _nmcli_compl_COMMAND_nl() { _nmcli_compl_PROPERTIES() { - while [[ "${#words[@]}" -gt 0 ]]; do - if [[ ${#words[@]} -le 1 ]]; then - local PREFIX="" - - if [[ "${words[0]:0:1}" == [+-] ]]; then - PREFIX="${words[0]:0:1}" - fi - _nmcli_list_nl "$(echo -e 'print\nquit\nyes' |nmcli c edit "$@" 2>/dev/null |awk -F: '/\..*:/ {print "'$PREFIX'"$1}')" - return 0 - elif [[ ${#words[@]} -le 2 ]]; then + while [[ ${#words[@]} -gt 0 ]]; do + if [[ ${#words[@]} -eq 1 ]]; then + _nmcli_list_nl "$(nmcli --complete-args connection modify "$@" "${words[@]}" 2>/dev/null)" return 0 fi _nmcli_array_delete_at words 0 1 done - _nmcli_list_nl "$(echo -e 'print\nquit\nyes' |nmcli c edit "$@" 2>/dev/null |awk -F: '/\..*:/ {print $1}')" + return 0 } _nmcli() @@ -802,22 +797,18 @@ _nmcli() cur='' fi - local OPTIONS_UNKNOWN_OPTION OPTIONS_TYPE OPTIONS_TYPED OPTIONS OPTIONS_MANDATORY COMMAND_ARGS_WAIT_OPTIONS OPTIONS_IP OPTIONS_MANDATORY OPTIONS_NEXT_GROUP OPTIONS_SEP OPTIONS_REPEATABLE - local COMMAND_CONNECTION_TYPE COMMAND_CONNECTION_ID OPTIONS_MANDATORY_IFNAME HELP_ONLY_AS_FIRST - local COMMAND_CONNECTION_ACTIVE="" + local OPTIONS OPTIONS_UNKNOWN_OPTION OPTIONS_TYPE OPTIONS_TYPED OPTIONS_IP OPTIONS_NEXT_GROUP \ + OPTIONS_SEP OPTIONS_REPEATABLE OPTIONS_MANDATORY OPTIONS_MANDATORY_IFNAME \ + COMMAND_ARGS_WAIT_OPTIONS COMMAND_CONNECTION_TYPE COMMAND_CONNECTION_ID \ + COMMAND_CONNECTION_ACTIVE="" HELP_ONLY_AS_FIRST="" \ + LONG_OPTIONS=(terse pretty mode fields colors escape ask show-secrets wait version help) - HELP_ONLY_AS_FIRST= - local LONG_OPTIONS=(terse pretty mode fields colors escape ask show-secrets wait version help) _nmcli_compl_OPTIONS i=$? - if [[ "$HELP_ONLY_AS_FIRST" == '0' ]]; then - # got a --help. No more completion. - return 0 - fi - case $i in 0) + # We have just completed an option or got an --help: terminate. return 0 ;; 1) @@ -1005,238 +996,10 @@ _nmcli() ;; a|ad|add) if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND "${words[2]}" type ifname con-name autoconnect master slave-type - elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(nmcli --complete-args connection add "" 2>/dev/null)" + else _nmcli_array_delete_at words 0 1 - - LONG_OPTIONS=(help) - HELP_ONLY_AS_FIRST=1 - _nmcli_compl_OPTIONS - case $? in - 0) - return 0 - ;; - 1) - if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then - _nmcli_compl_COMMAND "${words[2]}" type ifname con-name autoconnect master slave-type - fi - return 0 - ;; - esac - - OPTIONS_TYPE= - OPTIONS=(type ifname con-name autoconnect save master slave-type) - OPTIONS_MANDATORY=(type ifname) - COMMAND_ARGS_WAIT_OPTIONS=1 - OPTIONS_MANDATORY_IFNAME=1 - _nmcli_compl_ARGS && return 0 - - OPTIONS_MANDATORY_IFNAME= - if _nmcli_array_has_value OPTIONS "${OPTIONS_MANDATORY[@]}"; then - # we still have some missing mandatory options... - if [[ "$OPTIONS_UNKNOWN_OPTION" != '' ]]; then - if ! _nmcli_array_has_value OPTIONS "${OPTIONS_UNKNOWN_OPTION:1}"; then - # if we encountered an unknown option while having mandatory - # options, just return. - return 0 - fi - fi - _nmcli_list "$(echo "${OPTIONS[@]}")" - return 0 - fi - - OPTIONS_IP=(ip4 ip6 gw4 gw6) - OPTIONS_SEP=(--) - OPTIONS_MANDATORY=() - case "$OPTIONS_TYPE" in - 802-3|802-3-|802-3-e|802-3-et|802-3-eth|802-3-ethe|802-3-ether|802-3-ethern|802-3-etherne|802-3-ethernet| \ - e|et|eth|ethe|ether|ethern|etherne|ethernet) - OPTIONS_TYPE=ethernet - OPTIONS_TYPED=(mac cloned-mac mtu) - ;; - 802-11-w|802-11-wi|802-11-wir|802-11-wire|802-11-wirel|802-11-wirele|802-11-wireles|802-11-wireless| \ - wif|wifi) - OPTIONS_TYPE=wifi - OPTIONS_TYPED=(ssid mac cloned-mac mtu mode) - OPTIONS_MANDATORY=(ssid) - ;; - wim|wima|wimax) - OPTIONS_TYPE=wimax - OPTIONS_TYPED=(mac nsp) - ;; - g|gs|gsm) - OPTIONS_TYPE=gsm - OPTIONS_TYPED=(apn user password) - OPTIONS_MANDATORY=(apn) - ;; - c|cd|cdm|cdma) - OPTIONS_TYPE=cdma - OPTIONS_TYPED=(user password) - ;; - i|in|inf|infi|infin|infini|infinib|infiniba|infiniban|infiniband) - OPTIONS_TYPE=infiniband - OPTIONS_TYPED=(mac mtu transport-mode parent p-key) - ;; - bl|blu|blue|bluet|blueto|bluetoo|bluetoot|bluetooth) - OPTIONS_TYPE=bluetooth - OPTIONS_TYPED=(addr bt-type) - ;; - vl|vla|vlan) - OPTIONS_TYPE=vlan - OPTIONS_TYPED=(dev id flags ingress egress mtu) - OPTIONS_MANDATORY=(dev) - ;; - bond) - OPTIONS_TYPE=bond - OPTIONS_TYPED=(mode miimon downdelay updelay arp-interval arp-ip-target primary lacp-rate) - ;; - team) - OPTIONS_TYPE=team - OPTIONS_TYPED=(config) - ;; - bridge) - OPTIONS_TYPE=bridge - OPTIONS_TYPED=(stp priority forward-delay hello-time max-age ageing-time mac) - ;; - vp|vpn) - OPTIONS_TYPE=vpn - OPTIONS_TYPED=(vpn-type user) - OPTIONS_MANDATORY=(vpn-type) - ;; - 802-11-o|802-11-ol|802-11-olp|802-11-olpc|802-11-olpc-|802-11-olpc-m|802-11-olpc-me|802-11-olpc-mes|802-11-olpc-mesh| \ - o|ol|olp|olpc|olpc-|olpc-m|olpc-me|olpc-mes|olpc-mesh) - OPTIONS_TYPE=olpc-mesh - OPTIONS_TYPED=(ssid channel dhcp-anycast) - OPTIONS_MANDATORY=(ssid) - ;; - p|pp|ppp|pppo|pppoe) - OPTIONS_TYPE=pppoe - OPTIONS_TYPED=(username password service mtu mac) - OPTIONS_MANDATORY=(username) - ;; - a|ad|ads|adsl) - OPTIONS_TYPE=adsl - OPTIONS_TYPED=(username password protocol encapsulation) - OPTIONS_MANDATORY=(username protocol) - ;; - tu|tun) - OPTIONS_TYPE=tun - OPTIONS_TYPED=(mode owner group pi vnet-hdr multi-queue) - OPTIONS_MANDATORY=(mode) - ;; - ip|ip-|ip-t|ip-tu|ip-tun|ip-tunn|ip-tunne|ip-tunnel) - OPTIONS_TYPE=ip-tunnel - OPTIONS_TYPED=(mode remote local dev) - OPTIONS_MANDATORY=(mode remote) - ;; - m|ma|mac|macv|macvl|macvla|macvlan) - OPTIONS_TYPE=macvlan - OPTIONS_TYPED=(dev mode tap) - OPTIONS_MANDATORY=(dev mode) - ;; - vx|vxl|vxla|vxlan) - OPTIONS_TYPE=vxlan - OPTIONS_TYPED=(id remote local dev source-port-min source-port-max destination-port) - OPTIONS_MANDATORY=(id remote) - ;; - *) - # for an unknown connection type, we stop completion here - return 0 - ;; - esac - if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then - # means, we are at the end of options. Nothing more to parse, just show - # what are the options now. - if [[ "${#OPTIONS_MANDATORY[@]}" -gt 0 ]]; then - _nmcli_list "$(echo "${OPTIONS[@]}") $(echo "${OPTIONS_TYPED[@]}")" - else - _nmcli_list "$(echo "${OPTIONS[@]}") $(echo "${OPTIONS_TYPED[@]}") $(echo "${OPTIONS_IP[@]}") $(echo "${OPTIONS_SEP[@]}")" - fi - return 0 - fi - if [[ "${#OPTIONS[@]}" -gt 0 ]]; then - # we still have some options from before, but no mandatory ones. Mix them with OPTIONS_TYPED - # and continue parsing the options... - OPTIONS=("${OPTIONS[@]}" "${OPTIONS_TYPED[@]}") - OPTIONS_NEXT_GROUP=("${OPTIONS_TYPED[@]}") - _nmcli_compl_ARGS && return 0 - - if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then - # means, we are at the end of options. Nothing more to parse, just show - # what are the options now. - if [[ "${#OPTIONS_MANDATORY[@]}" -gt 0 ]]; then - _nmcli_list "$(echo "${OPTIONS[@]}")" - else - _nmcli_list "$(echo "${OPTIONS[@]}") $(echo "${OPTIONS_IP[@]}") $(echo "${OPTIONS_SEP[@]}")" - fi - return 0 - fi - - if [[ "$OPTIONS_UNKNOWN_OPTION" != "" ]]; then - # there was an unknown option specified. Maybe we have to stop with the completion. - if [[ "${#OPTIONS_MANDATORY[@]}" -gt 0 ]]; then - # we have an unknown option, but still mandatory ones that must be fullfiled first. - return 0 - fi - if ! (_nmcli_array_has_value OPTIONS_IP "${OPTIONS_UNKNOWN_OPTION:1}" || - _nmcli_array_has_value OPTIONS_SEP "${OPTIONS_UNKNOWN_OPTION:1}"); then - # the unknown option is neither an IP option nor a separator. - return 0 - fi - # The unknown option is an IP option or a separator, which is fine... continue... - fi - - fi - OPTIONS=("${OPTIONS_TYPED[@]}") - OPTIONS_NEXT_GROUP=() - - if [[ "${#OPTIONS_MANDATORY[@]}" -ge 1 ]]; then - # we have some mandatory options... don't check for IP options yet... - _nmcli_compl_ARGS && return 0 - - if _nmcli_array_has_value OPTIONS "${OPTIONS_MANDATORY[@]}"; then - _nmcli_list "$(echo "${OPTIONS[@]}")" - return 0 - fi - - if [[ "$OPTIONS_UNKNOWN_OPTION" != "" ]]; then - if ! (_nmcli_array_has_value OPTIONS_IP "${OPTIONS_UNKNOWN_OPTION:1}" || - _nmcli_array_has_value OPTIONS_SEP "${OPTIONS_UNKNOWN_OPTION:1}"); then - # the unknown option is neither an IP option nor a separator. - return 0 - fi - fi - fi - - - # no mandatory options... do final completion including IP options - OPTIONS=("${OPTIONS[@]}" "${OPTIONS_IP[@]}" "${OPTIONS_SEP[@]}") - OPTIONS_NEXT_GROUP=("${OPTIONS_IP[@]}" "${OPTIONS_SEP[@]}") - _nmcli_compl_ARGS && return 0 - - if [[ "$OPTIONS_UNKNOWN_OPTION" != "" ]]; then - return 0 - fi - - if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then - # means, we are at the end of options. Nothing more to parse, just show - # what are the options now. - _nmcli_list "$(echo "${OPTIONS[@]}")" - return 0 - fi - - - # process the last group of options, as the OPTIONS_TYPED are already handled... - OPTIONS=("${OPTIONS_IP[@]}" "${OPTIONS_SEP[@]}") - OPTIONS_NEXT_GROUP=("${OPTIONS_SEP[@]}") - COMMAND_ARGS_WAIT_OPTIONS=0 - _nmcli_compl_ARGS && return 0 - - _nmcli_array_delete_at words 0 - _nmcli_compl_PROPERTIES type "$OPTIONS_TYPE" - - return 0 - + _nmcli_list_nl "$(nmcli --complete-args connection add "${words[@]}" 2>/dev/null)" fi ;; e|ed|edi|edit) @@ -1421,7 +1184,7 @@ _nmcli() ;; d|de|dev|devi|devic|device) if [[ ${#words[@]} -eq 2 ]]; then - _nmcli_compl_COMMAND "$command" status show connect reapply disconnect delete monitor wifi set lldp + _nmcli_compl_COMMAND "$command" status show connect reapply modify disconnect delete monitor wifi set lldp elif [[ ${#words[@]} -gt 2 ]]; then case "$command" in s|st|sta|stat|statu|status) @@ -1436,6 +1199,14 @@ _nmcli() _nmcli_compl_COMMAND_nl "${words[2]}" "$(_nmcli_dev_status DEVICE)" fi ;; + mod|modi|modif|modify) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(nmcli --complete-args device modify "" 2>/dev/null)" + else + _nmcli_array_delete_at words 0 1 + _nmcli_list_nl "$(nmcli --complete-args device modify "${words[@]}" 2>/dev/null)" + fi + ;; d|di|dis|disc|disco|discon|disconn|disconne|disconnec|disconnect| \ de|del|dele|delet|delete| \ m|mo|mon|moni|monit|monito|monitor) diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c index 7e788654..8bc8828b 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -19,7 +19,6 @@ * Copyright 2010 - 2015 Red Hat, Inc. */ -/* Generated configuration file */ #include "nm-default.h" #include <stdio.h> @@ -41,6 +40,7 @@ #include "devices.h" #include "general.h" #include "agent.h" +#include "settings.h" #if defined(NM_DIST_VERSION) # define NMCLI_VERSION NM_DIST_VERSION @@ -64,6 +64,105 @@ GMainLoop *loop = NULL; static sigset_t signal_set; struct termios termios_orig; +static void +complete_field (GHashTable *h, const char *setting, NmcOutputField field[]) +{ + int i; + + for (i = 0; field[i].name; i++) { + if (setting) + g_hash_table_add (h, g_strdup_printf ("%s.%s", setting, field[i].name)); + else + g_hash_table_add (h, g_strdup (field[i].name)); + } +} + +static void +complete_one (gpointer key, gpointer value, gpointer user_data) +{ + const char *prefix = user_data; + const char *name = key; + const char *last; + + last = strrchr (prefix, ','); + if (last) + last++; + else + last = prefix; + + if ((!*last && !strchr (name, '.')) || matches (last, name) == 0) { + g_print ("%.*s%s%s\n", (int)(last-prefix), prefix, name, + strcmp (last, name) == 0 ? "," : ""); + } +} + +static void +complete_fields (const char *prefix) +{ + + GHashTable *h; + + h = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + complete_field (h, NULL, nmc_fields_ip4_config); + complete_field (h, NULL, nmc_fields_dhcp4_config); + complete_field (h, NULL, nmc_fields_ip6_config); + complete_field (h, NULL, nmc_fields_dhcp6_config); + complete_field (h, NULL, nmc_fields_con_show); + complete_field (h, NULL, nmc_fields_settings_names); + complete_field (h, NULL, nmc_fields_con_active_details_general); + complete_field (h, NULL, nmc_fields_con_active_details_vpn); + complete_field (h, NULL, nmc_fields_con_active_details_groups); + complete_field (h, NULL, nmc_fields_dev_status); + complete_field (h, NULL, nmc_fields_dev_show_general); + complete_field (h, NULL, nmc_fields_dev_show_connections); + complete_field (h, NULL, nmc_fields_dev_show_cap); + complete_field (h, NULL, nmc_fields_dev_show_wired_prop); + complete_field (h, NULL, nmc_fields_dev_show_wifi_prop); + complete_field (h, NULL, nmc_fields_dev_show_wimax_prop); + complete_field (h, NULL, nmc_fields_dev_wifi_list); + complete_field (h, NULL, nmc_fields_dev_wimax_list); + complete_field (h, NULL, nmc_fields_dev_show_master_prop); + complete_field (h, NULL, nmc_fields_dev_show_team_prop); + complete_field (h, NULL, nmc_fields_dev_show_vlan_prop); + complete_field (h, NULL, nmc_fields_dev_show_bluetooth); + complete_field (h, NULL, nmc_fields_dev_show_sections); + complete_field (h, NULL, nmc_fields_dev_lldp_list); + + complete_field (h, "connection", nmc_fields_setting_connection); + complete_field (h, "wired", nmc_fields_setting_wired); + complete_field (h, "8021X", nmc_fields_setting_8021X); + complete_field (h, "wireless", nmc_fields_setting_wireless); + complete_field (h, "wireless_security", nmc_fields_setting_wireless_security); + complete_field (h, "ip4-config", nmc_fields_setting_ip4_config); + complete_field (h, "ip6-config", nmc_fields_setting_ip6_config); + complete_field (h, "serial", nmc_fields_setting_serial); + complete_field (h, "ppp", nmc_fields_setting_ppp); + complete_field (h, "pppoe", nmc_fields_setting_pppoe); + complete_field (h, "adsl", nmc_fields_setting_adsl); + complete_field (h, "gsm", nmc_fields_setting_gsm); + complete_field (h, "cdma", nmc_fields_setting_cdma); + complete_field (h, "bluetooth", nmc_fields_setting_bluetooth); + complete_field (h, "olpc-mesh", nmc_fields_setting_olpc_mesh); + complete_field (h, "vpn", nmc_fields_setting_vpn); + complete_field (h, "wimax", nmc_fields_setting_wimax); + complete_field (h, "infiniband", nmc_fields_setting_infiniband); + complete_field (h, "bond", nmc_fields_setting_bond); + complete_field (h, "vlan", nmc_fields_setting_vlan); + complete_field (h, "bridge", nmc_fields_setting_bridge); + complete_field (h, "bridge-port", nmc_fields_setting_bridge_port); + complete_field (h, "team", nmc_fields_setting_team); + complete_field (h, "team0port", nmc_fields_setting_team_port); + complete_field (h, "dcb", nmc_fields_setting_dcb); + complete_field (h, "tun", nmc_fields_setting_tun); + complete_field (h, "ip-tunnel", nmc_fields_setting_ip_tunnel); + complete_field (h, "macvlan", nmc_fields_setting_macvlan); + complete_field (h, "vxlan", nmc_fields_setting_vxlan); + + g_hash_table_foreach (h, complete_one, (gpointer) prefix); + g_hash_table_destroy (h); +} + /* Get an error quark for use with GError */ GQuark @@ -78,9 +177,9 @@ nmcli_error_quark (void) } static void -usage (const char *prog_name) +usage (void) { - g_printerr (_("Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n" + g_printerr (_("Usage: nmcli [OPTIONS] OBJECT { COMMAND | help }\n" "\n" "OPTIONS\n" " -t[erse] terse output\n" @@ -103,48 +202,21 @@ usage (const char *prog_name) " d[evice] devices managed by NetworkManager\n" " a[gent] NetworkManager secret agent or polkit agent\n" " m[onitor] monitor NetworkManager changes\n" - "\n"), - prog_name); -} - -static NMCResultCode -do_help (NmCli *nmc, int argc, char **argv) -{ - usage ("nmcli"); - return NMC_RESULT_SUCCESS; + "\n")); } -static const struct cmd { - const char *cmd; - NMCResultCode (*func) (NmCli *nmc, int argc, char **argv); -} nmcli_cmds[] = { - { "general", do_general }, - { "monitor", do_monitor }, - { "networking", do_networking }, - { "radio", do_radio }, - { "connection", do_connections }, - { "device", do_devices }, - { "agent", do_agent }, - { "help", do_help }, - { 0 } +static const NMCCommand nmcli_cmds[] = { + { "general", do_general, NULL }, + { "monitor", do_monitor, NULL }, + { "networking", do_networking, NULL }, + { "radio", do_radio, NULL }, + { "connection", do_connections, NULL }, + { "device", do_devices, NULL }, + { "agent", do_agent, NULL }, + { NULL, do_overview, usage } }; static NMCResultCode -do_cmd (NmCli *nmc, const char *argv0, int argc, char **argv) -{ - const struct cmd *c; - - for (c = nmcli_cmds; c->cmd; ++c) { - if (matches (argv0, c->cmd) == 0) - return c->func (nmc, argc-1, argv+1); - } - - g_string_printf (nmc->return_text, _("Error: Object '%s' is unknown, try 'nmcli help'."), argv0); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; -} - -static NMCResultCode parse_command_line (NmCli *nmc, int argc, char **argv) { char *base; @@ -154,19 +226,34 @@ parse_command_line (NmCli *nmc, int argc, char **argv) base = argv[0]; else base++; + if (argc > 1 && nm_streq (argv[1], "--complete-args")) { + nmc->complete = TRUE; + argv[1] = argv[0]; + argc--; argv++; + } + argc--; argv++; /* parse options */ - while (argc > 1) { - char *opt = argv[1]; - /* '--' ends options */ - if (strcmp (opt, "--") == 0) { - argc--; argv++; - break; - } + while (argc) { + char *opt = argv[0]; if (opt[0] != '-') break; - if (opt[1] == '-') + + if (argc == 1 && nmc->complete) { + nmc_complete_strings (opt, "--terse", "--pretty", "--mode", "--colors", "--escape", + "--fields", "--nocheck", "--ask", "--show-secrets", + "--wait", "--version", "--help", NULL); + } + + if (opt[1] == '-') { opt++; + /* '--' ends options */ + if (opt[1] == '\0') { + argc--; argv++; + break; + } + } + if (matches (opt, "-terse") == 0) { if (nmc->print_output == NMC_PRINT_TERSE) { g_string_printf (nmc->return_text, _("Error: Option '--terse' is specified the second time.")); @@ -195,63 +282,67 @@ parse_command_line (NmCli *nmc, int argc, char **argv) nmc->print_output = NMC_PRINT_PRETTY; } else if (matches (opt, "-mode") == 0) { nmc->mode_specified = TRUE; - next_arg (&argc, &argv); - if (argc <= 1) { + if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } - if (matches (argv[1], "tabular") == 0) + if (argc == 1 && nmc->complete) + nmc_complete_strings (argv[0], "tabular", "multiline", NULL); + if (matches (argv[0], "tabular") == 0) nmc->multiline_output = FALSE; - else if (matches (argv[1], "multiline") == 0) + else if (matches (argv[0], "multiline") == 0) nmc->multiline_output = TRUE; else { - g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[1], opt); + g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[0], opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } } else if (matches (opt, "-colors") == 0) { - next_arg (&argc, &argv); - if (argc <= 1) { + if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } - if (matches (argv[1], "auto") == 0) + if (argc == 1 && nmc->complete) + nmc_complete_strings (argv[0], "yes", "no", "auto", NULL); + if (matches (argv[0], "auto") == 0) nmc->use_colors = NMC_USE_COLOR_AUTO; - else if (matches (argv[1], "yes") == 0) + else if (matches (argv[0], "yes") == 0) nmc->use_colors = NMC_USE_COLOR_YES; - else if (matches (argv[1], "no") == 0) + else if (matches (argv[0], "no") == 0) nmc->use_colors = NMC_USE_COLOR_NO; else { - g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[1], opt); + g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[0], opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } } else if (matches (opt, "-escape") == 0) { - next_arg (&argc, &argv); - if (argc <= 1) { + if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } - if (matches (argv[1], "yes") == 0) + if (argc == 1 && nmc->complete) + nmc_complete_strings (argv[0], "yes", "no", NULL); + if (matches (argv[0], "yes") == 0) nmc->escape_values = TRUE; - else if (matches (argv[1], "no") == 0) + else if (matches (argv[0], "no") == 0) nmc->escape_values = FALSE; else { - g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[1], opt); + g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[0], opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } } else if (matches (opt, "-fields") == 0) { - next_arg (&argc, &argv); - if (argc <= 1) { + if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: fields for '%s' options are missing."), opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } - nmc->required_fields = g_strdup (argv[1]); + if (argc == 1 && nmc->complete) + complete_fields (argv[0]); + nmc->required_fields = g_strdup (argv[0]); } else if (matches (opt, "-nocheck") == 0) { /* ignore for backward compatibility */ } else if (matches (opt, "-ask") == 0) { @@ -260,24 +351,25 @@ parse_command_line (NmCli *nmc, int argc, char **argv) nmc->show_secrets = TRUE; } else if (matches (opt, "-wait") == 0) { unsigned long timeout; - next_arg (&argc, &argv); - if (argc <= 1) { + if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } - if (!nmc_string_to_uint (argv[1], TRUE, 0, G_MAXINT, &timeout)) { + if (!nmc_string_to_uint (argv[0], TRUE, 0, G_MAXINT, &timeout)) { g_string_printf (nmc->return_text, _("Error: '%s' is not a valid timeout for '%s' option."), - argv[1], opt); + argv[0], opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } nmc->timeout = (int) timeout; } else if (matches (opt, "-version") == 0) { - g_print (_("nmcli tool, version %s\n"), NMCLI_VERSION); + if (!nmc->complete) + g_print (_("nmcli tool, version %s\n"), NMCLI_VERSION); return NMC_RESULT_SUCCESS; } else if (matches (opt, "-help") == 0) { - usage (base); + if (!nmc->complete) + usage (); return NMC_RESULT_SUCCESS; } else { g_string_printf (nmc->return_text, _("Error: Option '%s' is unknown, try 'nmcli -help'."), opt); @@ -288,13 +380,8 @@ parse_command_line (NmCli *nmc, int argc, char **argv) argv++; } - if (argc > 1) { - /* Now run the requested command */ - return do_cmd (nmc, argv[1], argc-1, argv+1); - } - - usage (base); - return nmc->return_value; + /* Now run the requested command */ + return nmc_do_cmd (nmc, nmcli_cmds, *argv, argc, argv); } static gboolean nmcli_sigint = FALSE; @@ -545,6 +632,7 @@ nmc_init (NmCli *nmc) nmc->output_data = g_ptr_array_new_full (20, g_free); memset (&nmc->print_fields, '\0', sizeof (NmcPrintFields)); nmc->ask = FALSE; + nmc->complete = FALSE; nmc->show_secrets = FALSE; nmc->use_colors = NMC_USE_COLOR_AUTO; nmc->in_editor = FALSE; @@ -628,8 +716,12 @@ main (int argc, char *argv[]) loop = g_main_loop_new (NULL, FALSE); /* create main loop */ g_main_loop_run (loop); /* run main loop */ - /* Print result descripting text */ - if (nm_cli.return_value != NMC_RESULT_SUCCESS) { + if (nm_cli.complete) { + /* Remove error statuses from command completion runs. */ + if (nm_cli.return_value < NMC_RESULT_COMPLETE_FILE) + nm_cli.return_value = NMC_RESULT_SUCCESS; + } else if (nm_cli.return_value != NMC_RESULT_SUCCESS) { + /* Print result descripting text */ g_printerr ("%s\n", nm_cli.return_text->str); } diff --git a/clients/cli/nmcli.h b/clients/cli/nmcli.h index 34100f00..a8908685 100644 --- a/clients/cli/nmcli.h +++ b/clients/cli/nmcli.h @@ -63,7 +63,10 @@ typedef enum { NMC_RESULT_ERROR_VERSIONS_MISMATCH = 9, /* Connection/Device/AP not found */ - NMC_RESULT_ERROR_NOT_FOUND = 10 + NMC_RESULT_ERROR_NOT_FOUND = 10, + + /* --complete-args signals a file name may follow */ + NMC_RESULT_COMPLETE_FILE = 65, } NMCResultCode; typedef enum { @@ -153,6 +156,7 @@ typedef struct _NmCli { GPtrArray *output_data; /* GPtrArray of arrays of NmcOutputField structs - accumulates data for output */ NmcPrintFields print_fields; /* Structure with field indices to print */ gboolean ask; /* Ask for missing parameters: option '--ask' */ + gboolean complete; /* Autocomplete the command line */ gboolean show_secrets; /* Whether to display secrets (both input and output): option '--show-secrets' */ gboolean in_editor; /* Whether running the editor - nmcli con edit' */ gboolean editor_status_line; /* Whether to display status line in connection editor */ diff --git a/clients/cli/polkit-agent.c b/clients/cli/polkit-agent.c index 8b56e953..086851de 100644 --- a/clients/cli/polkit-agent.c +++ b/clients/cli/polkit-agent.c @@ -126,7 +126,6 @@ nmc_start_polkit_agent_start_try (NmCli *nmc) #else /* polkit agent is not avalable; implement stub functions. */ -#include "nm-default.h" #include "nmcli.h" #include "polkit-agent.h" diff --git a/clients/cli/settings-docs.c b/clients/cli/settings-docs.c index 3616ff87..c376fd7a 100644 --- a/clients/cli/settings-docs.c +++ b/clients/cli/settings-docs.c @@ -16,11 +16,12 @@ NmcPropertyDesc setting_802_11_wireless[] = { { "band", "802.11 frequency band of the network. One of \"a\" for 5GHz 802.11a or \"bg\" for 2.4GHz 802.11. This will lock associations to the Wi-Fi network to the specific band, i.e. if \"a\" is specified, the device will not associate with the same network in the 2.4GHz band even if the network's settings are compatible. This setting depends on specific driver capability and may not work with all drivers." }, { "bssid", "If specified, directs the device to only associate with the given access point. This capability is highly driver dependent and not supported by all devices. Note: this property does not control the BSSID used when creating an Ad-Hoc network and is unlikely to in the future." }, { "channel", "Wireless channel to use for the Wi-Fi connection. The device will only join (or create for Ad-Hoc networks) a Wi-Fi network on the specified channel. Because channel numbers overlap between bands, this property also requires the \"band\" property to be set." }, - { "cloned-mac-address", "If specified, request that the Wi-Fi device use this MAC address instead of its permanent MAC address. This is known as MAC cloning or spoofing." }, + { "cloned-mac-address", "If specified, request that the device use this MAC address instead of its permanent MAC address. This is known as MAC cloning or spoofing. Beside explicitly specifing a MAC address, the special values \"preserve\", \"permanent\", \"random\" and \"stable\" are supported. \"preserve\" means not to touch the MAC address on activation. \"permanent\" means to use the permanent hardware address of the device. \"random\" creates a random MAC address on each connect. \"stable\" creates a hashed MAC address based on connection.stable-id (or the connection's UUID) and a machine dependent key. If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to \"permanent\". On D-Bus, this field is expressed as \"assigned-mac-address\" or the deprecated \"cloned-mac-address\"." }, + { "generate-mac-address-mask", "With \"cloned-mac-address\" setting \"random\" or \"stable\", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting \"FE:FF:FF:00:00:00\" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the \"random\" or \"stable\" algorithm. If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of \"FE:FF:FF:00:00:00 68:F7:28:00:00:00\" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of \"02:00:00:00:00:00 00:00:00:00:00:00\" will create a fully scrambled globally-administered, burned-in MAC address. If the value contains more then one additional MAC addresses, one of them is chosen randomly. For example, \"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00\" will create a fully scrambled MAC address, randomly locally or globally administered." }, { "hidden", "If TRUE, indicates this network is a non-broadcasting network that hides its SSID. In this case various workarounds may take place, such as probe-scanning the SSID for more reliable network discovery. However, these workarounds expose inherent insecurities with hidden SSID networks, and thus hidden SSID networks should be used with caution." }, { "mac-address", "If specified, this connection will only apply to the Wi-Fi device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing)." }, { "mac-address-blacklist", "A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply. Each MAC address should be given in the standard hex-digits-and-colons notation (eg \"00:11:22:33:44:55\")." }, - { "mac-address-randomization", "One of NM_SETTING_MAC_RANDOMIZATION_DEFAULT (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization), NM_SETTING_MAC_RANDOMIZATION_NEVER (1) (never randomize the MAC address), or NM_SETTING_MAC_RANDOMIZATION_ALWAYS (2) (always randomize the MAC address)." }, + { "mac-address-randomization", "One of NM_SETTING_MAC_RANDOMIZATION_DEFAULT (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization), NM_SETTING_MAC_RANDOMIZATION_NEVER (1) (never randomize the MAC address), or NM_SETTING_MAC_RANDOMIZATION_ALWAYS (2) (always randomize the MAC address). Deprecated: 1" }, { "mode", "Wi-Fi network mode; one of \"infrastructure\", \"adhoc\" or \"ap\". If blank, infrastructure is assumed." }, { "mtu", "If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames." }, { "name", "The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\"." }, @@ -92,8 +93,9 @@ NmcPropertyDesc setting_802_1x[] = { NmcPropertyDesc setting_802_3_ethernet[] = { { "auto-negotiate", "If TRUE, allow auto-negotiation of port speed and duplex mode. If FALSE, do not allow auto-negotiation, in which case the \"speed\" and \"duplex\" properties should be set." }, - { "cloned-mac-address", "If specified, request that the device use this MAC address instead of its permanent MAC address. This is known as MAC cloning or spoofing." }, + { "cloned-mac-address", "If specified, request that the device use this MAC address instead of its permanent MAC address. This is known as MAC cloning or spoofing. Beside explicitly specifing a MAC address, the special values \"preserve\", \"permanent\", \"random\" and \"stable\" are supported. \"preserve\" means not to touch the MAC address on activation. \"permanent\" means to use the permanent hardware address of the device. \"random\" creates a random MAC address on each connect. \"stable\" creates a hashed MAC address based on connection.stable-id (or the connection's UUID) and a machine dependent key. If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to \"permanent\". On D-Bus, this field is expressed as \"assigned-mac-address\" or the deprecated \"cloned-mac-address\"." }, { "duplex", "If specified, request that the device only use the specified duplex mode. Either \"half\" or \"full\"." }, + { "generate-mac-address-mask", "With \"cloned-mac-address\" setting \"random\" or \"stable\", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting \"FE:FF:FF:00:00:00\" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the \"random\" or \"stable\" algorithm. If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of \"FE:FF:FF:00:00:00 68:F7:28:00:00:00\" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of \"02:00:00:00:00:00 00:00:00:00:00:00\" will create a fully scrambled globally-administered, burned-in MAC address. If the value contains more then one additional MAC addresses, one of them is chosen randomly. For example, \"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00\" will create a fully scrambled MAC address, randomly locally or globally administered." }, { "mac-address", "If specified, this connection will only apply to the Ethernet device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing)." }, { "mac-address-blacklist", "If specified, this connection will never apply to the Ethernet device whose permanent MAC address matches an address in the list. Each MAC address is in the standard hex-digits-and-colons notation (00:11:22:33:44:55)." }, { "mtu", "If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames." }, @@ -171,6 +173,7 @@ NmcPropertyDesc setting_connection[] = { { "read-only", "FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified." }, { "secondaries", "List of connection UUIDs that should be activated when the base connection itself is activated. Currently only VPN connections are supported." }, { "slave-type", "Setting name of the device type of this slave's master connection (eg, \"bond\"), or NULL if this connection is not a slave." }, + { "stable-id", "This token to generate stable IDs for the connection. If unset, the UUID will be used instead. The stable-id is used instead of the connection UUID for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable." }, { "timestamp", "The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved)." }, { "type", "Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, \"802-3-ethernet\" or \"802-11-wireless\" or \"bluetooth\", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, \"vpn\" or \"bridge\", etc)." }, { "uuid", "A universally unique identifier for the connection, for example generated with libuuid. It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network. For example, it should not be changed when the \"id\" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or \"type\" property changes. The UUID must be in the format \"2815492f-7e56-435e-b2e9-246bd7cdc664\" (ie, contains only hexadecimal characters and \"-\")." }, @@ -285,6 +288,7 @@ NmcPropertyDesc setting_ipv6[] = { { "never-default", "If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager." }, { "route-metric", "The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is choosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric." }, { "routes", "Array of IP routes." }, + { "token", "Configure the token for draft-chown-6man-tokenised-ipv6-identifiers-02 IPv6 tokenized interface identifiers. Useful with eui64 addr-gen-mode." }, }; NmcPropertyDesc setting_macvlan[] = { @@ -409,24 +413,24 @@ typedef struct { NmcSettingDesc all_settings[] = { { "802-11-olpc-mesh", setting_802_11_olpc_mesh, 4 }, - { "802-11-wireless", setting_802_11_wireless, 16 }, + { "802-11-wireless", setting_802_11_wireless, 17 }, { "802-11-wireless-security", setting_802_11_wireless_security, 18 }, { "802-1x", setting_802_1x, 35 }, - { "802-3-ethernet", setting_802_3_ethernet, 14 }, + { "802-3-ethernet", setting_802_3_ethernet, 15 }, { "adsl", setting_adsl, 8 }, { "bluetooth", setting_bluetooth, 3 }, { "bond", setting_bond, 2 }, { "bridge", setting_bridge, 9 }, { "bridge-port", setting_bridge_port, 4 }, { "cdma", setting_cdma, 5 }, - { "connection", setting_connection, 18 }, + { "connection", setting_connection, 19 }, { "dcb", setting_dcb, 16 }, { "generic", setting_generic, 1 }, { "gsm", setting_gsm, 13 }, { "infiniband", setting_infiniband, 6 }, { "ip-tunnel", setting_ip_tunnel, 13 }, { "ipv4", setting_ipv4, 20 }, - { "ipv6", setting_ipv6, 20 }, + { "ipv6", setting_ipv6, 21 }, { "macvlan", setting_macvlan, 5 }, { "ppp", setting_ppp, 19 }, { "pppoe", setting_pppoe, 5 }, diff --git a/clients/cli/settings.c b/clients/cli/settings.c index f2a504cc..ee167d2c 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -24,8 +24,10 @@ #include <stdlib.h> #include <arpa/inet.h> +#include "nm-common-macros.h" #include "utils.h" #include "common.h" +#include "nm-vpn-helpers.h" /* Forward declarations */ static char *wep_key_type_to_string (NMWepKeyType type); @@ -43,26 +45,28 @@ NmcOutputField nmc_fields_setting_connection[] = { SETTING_FIELD ("name"), /* 0 */ SETTING_FIELD (NM_SETTING_CONNECTION_ID), /* 1 */ SETTING_FIELD (NM_SETTING_CONNECTION_UUID), /* 2 */ - SETTING_FIELD (NM_SETTING_CONNECTION_INTERFACE_NAME), /* 3 */ - SETTING_FIELD (NM_SETTING_CONNECTION_TYPE), /* 4 */ - SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT), /* 5 */ - SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY), /* 6 */ - SETTING_FIELD (NM_SETTING_CONNECTION_TIMESTAMP), /* 7 */ - SETTING_FIELD (NM_SETTING_CONNECTION_READ_ONLY), /* 8 */ - SETTING_FIELD (NM_SETTING_CONNECTION_PERMISSIONS), /* 9 */ - SETTING_FIELD (NM_SETTING_CONNECTION_ZONE), /* 10 */ - SETTING_FIELD (NM_SETTING_CONNECTION_MASTER), /* 11 */ - SETTING_FIELD (NM_SETTING_CONNECTION_SLAVE_TYPE), /* 12 */ - SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES), /* 13 */ - SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES), /* 14 */ - SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT), /* 15 */ - SETTING_FIELD (NM_SETTING_CONNECTION_METERED), /* 16 */ - SETTING_FIELD (NM_SETTING_CONNECTION_LLDP), /* 17 */ + SETTING_FIELD (NM_SETTING_CONNECTION_STABLE_ID), /* 3 */ + SETTING_FIELD (NM_SETTING_CONNECTION_INTERFACE_NAME), /* 4 */ + SETTING_FIELD (NM_SETTING_CONNECTION_TYPE), /* 5 */ + SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT), /* 6 */ + SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY), /* 7 */ + SETTING_FIELD (NM_SETTING_CONNECTION_TIMESTAMP), /* 8 */ + SETTING_FIELD (NM_SETTING_CONNECTION_READ_ONLY), /* 9 */ + SETTING_FIELD (NM_SETTING_CONNECTION_PERMISSIONS), /* 10 */ + SETTING_FIELD (NM_SETTING_CONNECTION_ZONE), /* 11 */ + SETTING_FIELD (NM_SETTING_CONNECTION_MASTER), /* 12 */ + SETTING_FIELD (NM_SETTING_CONNECTION_SLAVE_TYPE), /* 13 */ + SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES), /* 14 */ + SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES), /* 15 */ + SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT), /* 16 */ + SETTING_FIELD (NM_SETTING_CONNECTION_METERED), /* 17 */ + SETTING_FIELD (NM_SETTING_CONNECTION_LLDP), /* 18 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_CONNECTION_ALL "name"","\ NM_SETTING_CONNECTION_ID","\ NM_SETTING_CONNECTION_UUID","\ + NM_SETTING_CONNECTION_STABLE_ID","\ NM_SETTING_CONNECTION_INTERFACE_NAME","\ NM_SETTING_CONNECTION_TYPE","\ NM_SETTING_CONNECTION_AUTOCONNECT","\ @@ -88,13 +92,14 @@ NmcOutputField nmc_fields_setting_wired[] = { SETTING_FIELD (NM_SETTING_WIRED_AUTO_NEGOTIATE), /* 4 */ SETTING_FIELD (NM_SETTING_WIRED_MAC_ADDRESS), /* 5 */ SETTING_FIELD (NM_SETTING_WIRED_CLONED_MAC_ADDRESS), /* 6 */ - SETTING_FIELD (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST), /* 7 */ - SETTING_FIELD (NM_SETTING_WIRED_MTU), /* 8 */ - SETTING_FIELD (NM_SETTING_WIRED_S390_SUBCHANNELS), /* 9 */ - SETTING_FIELD (NM_SETTING_WIRED_S390_NETTYPE), /* 10 */ - SETTING_FIELD (NM_SETTING_WIRED_S390_OPTIONS), /* 11 */ - SETTING_FIELD (NM_SETTING_WIRED_WAKE_ON_LAN), /* 12 */ - SETTING_FIELD (NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD), /* 13 */ + SETTING_FIELD (NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK), /* 7 */ + SETTING_FIELD (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST), /* 8 */ + SETTING_FIELD (NM_SETTING_WIRED_MTU), /* 9 */ + SETTING_FIELD (NM_SETTING_WIRED_S390_SUBCHANNELS), /* 10 */ + SETTING_FIELD (NM_SETTING_WIRED_S390_NETTYPE), /* 11 */ + SETTING_FIELD (NM_SETTING_WIRED_S390_OPTIONS), /* 12 */ + SETTING_FIELD (NM_SETTING_WIRED_WAKE_ON_LAN), /* 13 */ + SETTING_FIELD (NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD), /* 14 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_WIRED_ALL "name"","\ @@ -104,6 +109,7 @@ NmcOutputField nmc_fields_setting_wired[] = { NM_SETTING_WIRED_AUTO_NEGOTIATE","\ NM_SETTING_WIRED_MAC_ADDRESS","\ NM_SETTING_WIRED_CLONED_MAC_ADDRESS","\ + NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK","\ NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST","\ NM_SETTING_WIRED_MTU","\ NM_SETTING_WIRED_S390_SUBCHANNELS","\ @@ -199,12 +205,13 @@ NmcOutputField nmc_fields_setting_wireless[] = { SETTING_FIELD (NM_SETTING_WIRELESS_TX_POWER), /* 7 */ SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS), /* 8 */ SETTING_FIELD (NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS), /* 9 */ - SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST), /* 10 */ - SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION), /* 11 */ - SETTING_FIELD (NM_SETTING_WIRELESS_MTU), /* 12 */ - SETTING_FIELD (NM_SETTING_WIRELESS_SEEN_BSSIDS), /* 13 */ - SETTING_FIELD (NM_SETTING_WIRELESS_HIDDEN), /* 14 */ - SETTING_FIELD (NM_SETTING_WIRELESS_POWERSAVE), /* 15 */ + SETTING_FIELD (NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK), /* 10 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST), /* 11 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION), /* 12 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MTU), /* 13 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SEEN_BSSIDS), /* 14 */ + SETTING_FIELD (NM_SETTING_WIRELESS_HIDDEN), /* 15 */ + SETTING_FIELD (NM_SETTING_WIRELESS_POWERSAVE), /* 16 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_WIRELESS_ALL "name"","\ @@ -217,6 +224,7 @@ NmcOutputField nmc_fields_setting_wireless[] = { NM_SETTING_WIRELESS_TX_POWER","\ NM_SETTING_WIRELESS_MAC_ADDRESS","\ NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS","\ + NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK","\ NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST","\ NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION","\ NM_SETTING_WIRELESS_MTU","\ @@ -330,6 +338,7 @@ NmcOutputField nmc_fields_setting_ip6_config[] = { SETTING_FIELD (NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE), /* 15 */ SETTING_FIELD (NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME), /* 16 */ SETTING_FIELD (NM_SETTING_IP_CONFIG_DHCP_HOSTNAME), /* 17 */ + SETTING_FIELD (NM_SETTING_IP6_CONFIG_TOKEN), /* 18 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_IP6_CONFIG_ALL "name"","\ @@ -349,7 +358,8 @@ NmcOutputField nmc_fields_setting_ip6_config[] = { NM_SETTING_IP6_CONFIG_IP6_PRIVACY","\ NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE","\ NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME","\ - NM_SETTING_IP_CONFIG_DHCP_HOSTNAME + NM_SETTING_IP_CONFIG_DHCP_HOSTNAME","\ + NM_SETTING_IP6_CONFIG_TOKEN /* Available fields for NM_SETTING_SERIAL_SETTING_NAME */ NmcOutputField nmc_fields_setting_serial[] = { @@ -1249,6 +1259,7 @@ DEFINE_SECRET_FLAGS_GETTER (nmc_property_cdma_get_password_flags, NM_SETTING_CDM /* --- NM_SETTING_CONNECTION_SETTING_NAME property get functions --- */ DEFINE_GETTER (nmc_property_connection_get_id, NM_SETTING_CONNECTION_ID) DEFINE_GETTER (nmc_property_connection_get_uuid, NM_SETTING_CONNECTION_UUID) +DEFINE_GETTER (nmc_property_connection_get_stable_id, NM_SETTING_CONNECTION_STABLE_ID) DEFINE_GETTER (nmc_property_connection_get_interface_name, NM_SETTING_CONNECTION_INTERFACE_NAME) DEFINE_GETTER (nmc_property_connection_get_type, NM_SETTING_CONNECTION_TYPE) DEFINE_GETTER (nmc_property_connection_get_autoconnect, NM_SETTING_CONNECTION_AUTOCONNECT) @@ -1629,6 +1640,7 @@ DEFINE_GETTER (nmc_property_ipv6_get_never_default, NM_SETTING_IP_CONFIG_NEVER_D DEFINE_GETTER (nmc_property_ipv6_get_may_fail, NM_SETTING_IP_CONFIG_MAY_FAIL) DEFINE_GETTER (nmc_property_ipv6_get_dhcp_send_hostname, NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME) DEFINE_GETTER (nmc_property_ipv6_get_dhcp_hostname, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME) +DEFINE_GETTER (nmc_property_ipv6_get_token, NM_SETTING_IP6_CONFIG_TOKEN) static char * nmc_property_ipv6_get_ip6_privacy (NMSetting *setting, NmcPropertyGetType get_type) @@ -1757,6 +1769,7 @@ DEFINE_GETTER (nmc_property_wired_get_duplex, NM_SETTING_WIRED_DUPLEX) DEFINE_GETTER (nmc_property_wired_get_auto_negotiate, NM_SETTING_WIRED_AUTO_NEGOTIATE) DEFINE_GETTER (nmc_property_wired_get_mac_address, NM_SETTING_WIRED_MAC_ADDRESS) DEFINE_GETTER (nmc_property_wired_get_cloned_mac_address, NM_SETTING_WIRED_CLONED_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_wired_get_generate_mac_address_mask, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK) DEFINE_GETTER (nmc_property_wired_get_mac_address_blacklist, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST) DEFINE_GETTER (nmc_property_wired_get_s390_subchannels, NM_SETTING_WIRED_S390_SUBCHANNELS) DEFINE_GETTER (nmc_property_wired_get_s390_nettype, NM_SETTING_WIRED_S390_NETTYPE) @@ -1883,6 +1896,7 @@ DEFINE_GETTER (nmc_property_wireless_get_rate, NM_SETTING_WIRELESS_RATE) DEFINE_GETTER (nmc_property_wireless_get_tx_power, NM_SETTING_WIRELESS_TX_POWER) DEFINE_GETTER (nmc_property_wireless_get_mac_address, NM_SETTING_WIRELESS_MAC_ADDRESS) DEFINE_GETTER (nmc_property_wireless_get_cloned_mac_address, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_wireless_get_generate_mac_address_mask, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK) DEFINE_GETTER (nmc_property_wireless_get_mac_address_blacklist, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST) DEFINE_GETTER (nmc_property_wireless_get_seen_bssids, NM_SETTING_WIRELESS_SEEN_BSSIDS) DEFINE_GETTER (nmc_property_wireless_get_hidden, NM_SETTING_WIRELESS_HIDDEN) @@ -2402,21 +2416,58 @@ nmc_setting_custom_init (NMSetting *setting) { g_return_if_fail (NM_IS_SETTING (setting)); - if (NM_IS_SETTING_IP4_CONFIG (setting)) { + if (NM_IS_SETTING_VLAN (setting)) { + /* Set sensible initial VLAN values */ + g_object_set (NM_SETTING_VLAN (setting), + NM_SETTING_VLAN_ID, 1, + NULL); + } else if (NM_IS_SETTING_INFINIBAND (setting)) { + /* Initialize 'transport-mode' so that 'infiniband' is valid */ + g_object_set (NM_SETTING_INFINIBAND (setting), + NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", + NULL); + } else if (NM_IS_SETTING_CDMA (setting)) { + /* Initialize 'number' so that 'cdma' is valid */ + g_object_set (NM_SETTING_CDMA (setting), + NM_SETTING_CDMA_NUMBER, "#777", + NULL); + } else if (NM_IS_SETTING_GSM (setting)) { + /* Initialize 'number' so that 'gsm' is valid */ + g_object_set (NM_SETTING_GSM (setting), + NM_SETTING_GSM_NUMBER, "*99#", + NULL); + } else if (NM_IS_SETTING_OLPC_MESH (setting)) { + g_object_set (NM_SETTING_OLPC_MESH (setting), + NM_SETTING_OLPC_MESH_CHANNEL, 1, + NULL); + } else if (NM_IS_SETTING_WIRELESS (setting)) { + /* For Wi-Fi set mode to "infrastructure". Even though mode == NULL + * is regarded as "infrastructure", explicit value makes no doubts. + */ + g_object_set (NM_SETTING_WIRELESS (setting), + NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, + NULL); + } else if (NM_IS_SETTING_ADSL (setting)) { + /* Initialize a protocol */ + g_object_set (NM_SETTING_ADSL (setting), + NM_SETTING_ADSL_PROTOCOL, NM_SETTING_ADSL_PROTOCOL_PPPOE, + NULL); + } else if (NM_IS_SETTING_IP4_CONFIG (setting)) { g_object_set (NM_SETTING_IP_CONFIG (setting), NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - nmc_setting_ip4_connect_handlers (NM_SETTING_IP_CONFIG (setting)); } else if (NM_IS_SETTING_IP6_CONFIG (setting)) { g_object_set (NM_SETTING_IP_CONFIG (setting), NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); - nmc_setting_ip6_connect_handlers (NM_SETTING_IP_CONFIG (setting)); - } else if (NM_IS_SETTING_WIRELESS (setting)) { - g_object_set (NM_SETTING_WIRELESS (setting), - NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, + } else if (NM_IS_SETTING_TUN (setting)) { + g_object_set (NM_SETTING_TUN (setting), + NM_SETTING_TUN_MODE, NM_SETTING_TUN_MODE_TUN, + NULL); + } else if (NM_IS_SETTING_BLUETOOTH (setting)) { + g_object_set (NM_SETTING_BLUETOOTH (setting), + NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU, NULL); - nmc_setting_wireless_connect_handlers (NM_SETTING_WIRELESS (setting)); } } @@ -2842,11 +2893,12 @@ nmc_property_set_ssid (NMSetting *setting, const char *prop, const char *val, GE } static gboolean -nmc_property_set_mac (NMSetting *setting, const char *prop, const char *val, GError **error) +_property_set_mac (NMSetting *setting, const char *prop, const char *val, gboolean cloned_mac_addr, GError **error) { g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - if (!nm_utils_hwaddr_valid (val, ETH_ALEN)) { + if ( (!cloned_mac_addr || !NM_CLONED_MAC_IS_SPECIAL (val)) + && !nm_utils_hwaddr_valid (val, ETH_ALEN)) { g_set_error (error, 1, 0, _("'%s' is not a valid Ethernet MAC"), val); return FALSE; } @@ -2856,6 +2908,18 @@ nmc_property_set_mac (NMSetting *setting, const char *prop, const char *val, GEr } static gboolean +nmc_property_set_mac (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return _property_set_mac (setting, prop, val, FALSE, error); +} + +static gboolean +nmc_property_set_mac_cloned (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return _property_set_mac (setting, prop, val, TRUE, error); +} + +static gboolean nmc_property_set_mtu (NMSetting *setting, const char *prop, const char *val, GError **error) { const char *mtu = val; @@ -2916,6 +2980,16 @@ nmc_property_set_secret_flags (NMSetting *setting, const char *prop, const char } static gboolean +nmc_property_set_vpn_service (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + gs_free char *service_name = NULL; + + service_name = nm_vpn_plugin_info_list_find_service_type (nm_vpn_get_plugin_infos (), val); + g_object_set (setting, prop, service_name ? : val, NULL); + return TRUE; +} + +static gboolean nmc_util_is_domain (const char *domain) { //FIXME: implement @@ -2994,6 +3068,32 @@ done: } /* --- NM_SETTING_CONNECTION_SETTING_NAME property setter functions --- */ +static gboolean +nmc_property_connection_set_type (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + gs_free char *uuid = NULL; + + if (nm_setting_connection_get_uuid (NM_SETTING_CONNECTION (setting))) { + /* Don't allow setting type unless the connection is brand new. + * Just because it's a bad idea and the user wouldn't probably want that. + * No technical reason, really. + * Also, using uuid to see if the connection is brand new is a bit + * hacky: we can not see if the type is already set, because + * nmc_setting_set_property() is called only after the property + * we're setting (type) has been removed. */ + g_set_error (error, 1, 0, _("Can not change the connection type")); + return FALSE; + } + + uuid = nm_utils_uuid_generate (); + g_object_set (G_OBJECT (setting), + NM_SETTING_CONNECTION_UUID, uuid, + NULL); + + g_object_set (G_OBJECT (setting), prop, val, NULL); + return TRUE; +} + #if 0 /* * Setting/removing UUID has been forbidden. @@ -3144,8 +3244,7 @@ nmc_property_connection_set_secondaries (NMSetting *setting, const char *prop, c continue; if (nm_utils_is_uuid (*iter)) { - con = nmc_find_connection (nm_cli.connections, - "uuid", *iter, NULL); + con = nmc_find_connection (nm_cli.connections, "uuid", *iter, NULL, FALSE); if (!con) g_print (_("Warning: %s is not an UUID of any existing connection profile\n"), *iter); else { @@ -3157,8 +3256,7 @@ nmc_property_connection_set_secondaries (NMSetting *setting, const char *prop, c } } } else { - con = nmc_find_connection (nm_cli.connections, - "id", *iter, NULL); + con = nmc_find_connection (nm_cli.connections, "id", *iter, NULL, FALSE); if (!con) { g_set_error (error, 1, 0, _("'%s' is not a name of any exiting profile"), *iter); g_strfreev (strv); @@ -6137,6 +6235,13 @@ nmc_properties_init (void) NULL, NULL, NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, STABLE_ID), + nmc_property_connection_get_stable_id, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); nmc_add_prop_funcs (GLUE (CONNECTION, INTERFACE_NAME), nmc_property_connection_get_interface_name, nmc_property_set_ifname, @@ -6146,7 +6251,7 @@ nmc_properties_init (void) NULL); nmc_add_prop_funcs (GLUE (CONNECTION, TYPE), nmc_property_connection_get_type, - NULL, /* read-only */ + nmc_property_connection_set_type, NULL, NULL, NULL, @@ -6728,6 +6833,13 @@ nmc_properties_init (void) NULL, NULL, NULL); + nmc_add_prop_funcs (GLUE (IP6_CONFIG, TOKEN), + nmc_property_ipv6_get_token, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); /* Add editable properties for NM_SETTING_OLPC_MESH_SETTING_NAME */ nmc_add_prop_funcs (GLUE (OLPC_MESH, SSID), @@ -7005,7 +7117,7 @@ nmc_properties_init (void) /* Add editable properties for NM_SETTING_VPN_SETTING_NAME */ nmc_add_prop_funcs (GLUE (VPN, SERVICE_TYPE), nmc_property_vpn_get_service_type, - nmc_property_set_string, + nmc_property_set_vpn_service, NULL, NULL, NULL, @@ -7101,7 +7213,14 @@ nmc_properties_init (void) NULL); nmc_add_prop_funcs (GLUE (WIRED, CLONED_MAC_ADDRESS), nmc_property_wired_get_cloned_mac_address, - nmc_property_set_mac, + nmc_property_set_mac_cloned, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRED, GENERATE_MAC_ADDRESS_MASK), + nmc_property_wired_get_generate_mac_address_mask, + nmc_property_set_string, NULL, NULL, NULL, @@ -7219,7 +7338,14 @@ nmc_properties_init (void) NULL); nmc_add_prop_funcs (GLUE (WIRELESS, CLONED_MAC_ADDRESS), nmc_property_wireless_get_cloned_mac_address, - nmc_property_set_mac, + nmc_property_set_mac_cloned, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, GENERATE_MAC_ADDRESS_MASK), + nmc_property_wireless_get_generate_mac_address_mask, + nmc_property_set_string, NULL, NULL, NULL, @@ -7976,21 +8102,22 @@ setting_connection_details (NMSetting *setting, NmCli *nmc, const char *one_pro set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); set_val_str (arr, 1, nmc_property_connection_get_id (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 2, nmc_property_connection_get_uuid (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 3, nmc_property_connection_get_interface_name (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 4, nmc_property_connection_get_type (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 5, nmc_property_connection_get_autoconnect (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 6, nmc_property_connection_get_autoconnect_priority (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 7, nmc_property_connection_get_timestamp (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 8, nmc_property_connection_get_read_only (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 9, nmc_property_connection_get_permissions (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 10, nmc_property_connection_get_zone (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 11, nmc_property_connection_get_master (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 12, nmc_property_connection_get_slave_type (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 13, nmc_property_connection_get_autoconnect_slaves (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 14, nmc_property_connection_get_secondaries (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 15, nmc_property_connection_get_gateway_ping_timeout (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 16, nmc_property_connection_get_metered (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 17, nmc_property_connection_get_lldp (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 3, nmc_property_connection_get_stable_id (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 4, nmc_property_connection_get_interface_name (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 5, nmc_property_connection_get_type (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 6, nmc_property_connection_get_autoconnect (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 7, nmc_property_connection_get_autoconnect_priority (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 8, nmc_property_connection_get_timestamp (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 9, nmc_property_connection_get_read_only (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 10, nmc_property_connection_get_permissions (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 11, nmc_property_connection_get_zone (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 12, nmc_property_connection_get_master (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 13, nmc_property_connection_get_slave_type (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 14, nmc_property_connection_get_autoconnect_slaves (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 15, nmc_property_connection_get_secondaries (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 16, nmc_property_connection_get_gateway_ping_timeout (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 17, nmc_property_connection_get_metered (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 18, nmc_property_connection_get_lldp (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ @@ -8022,13 +8149,14 @@ setting_wired_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gb set_val_str (arr, 4, nmc_property_wired_get_auto_negotiate (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 5, nmc_property_wired_get_mac_address (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 6, nmc_property_wired_get_cloned_mac_address (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 7, nmc_property_wired_get_mac_address_blacklist (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 8, nmc_property_wired_get_mtu (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 9, nmc_property_wired_get_s390_subchannels (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 10, nmc_property_wired_get_s390_nettype (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 11, nmc_property_wired_get_s390_options (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 12, nmc_property_wired_get_wake_on_lan (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 13, nmc_property_wired_get_wake_on_lan_password (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 7, nmc_property_wired_get_generate_mac_address_mask (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 8, nmc_property_wired_get_mac_address_blacklist (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 9, nmc_property_wired_get_mtu (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 10, nmc_property_wired_get_s390_subchannels (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 11, nmc_property_wired_get_s390_nettype (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 12, nmc_property_wired_get_s390_options (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 13, nmc_property_wired_get_wake_on_lan (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 14, nmc_property_wired_get_wake_on_lan_password (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ @@ -8122,12 +8250,13 @@ setting_wireless_details (NMSetting *setting, NmCli *nmc, const char *one_prop, set_val_str (arr, 7, nmc_property_wireless_get_tx_power (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 8, nmc_property_wireless_get_mac_address (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 9, nmc_property_wireless_get_cloned_mac_address (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 10, nmc_property_wireless_get_mac_address_blacklist (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 11, nmc_property_wireless_get_mac_address_randomization (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 12, nmc_property_wireless_get_mtu (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 13, nmc_property_wireless_get_seen_bssids (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 14, nmc_property_wireless_get_hidden (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 15, nmc_property_wireless_get_powersave (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 10, nmc_property_wireless_get_generate_mac_address_mask (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 11, nmc_property_wireless_get_mac_address_blacklist (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 12, nmc_property_wireless_get_mac_address_randomization (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 13, nmc_property_wireless_get_mtu (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 14, nmc_property_wireless_get_seen_bssids (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 15, nmc_property_wireless_get_hidden (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 16, nmc_property_wireless_get_powersave (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ @@ -8256,6 +8385,7 @@ setting_ip6_config_details (NMSetting *setting, NmCli *nmc, const char *one_pro set_val_str (arr, 15, nmc_property_ipv6_get_addr_gen_mode (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 16, nmc_property_ipv6_get_dhcp_send_hostname (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 17, nmc_property_ipv6_get_dhcp_hostname (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 18, nmc_property_ipv6_get_token (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ diff --git a/clients/cli/settings.h b/clients/cli/settings.h index bab00ee9..33900510 100644 --- a/clients/cli/settings.h +++ b/clients/cli/settings.h @@ -63,4 +63,34 @@ gboolean nmc_property_set_gvalue (NMSetting *setting, const char *prop, GValue * gboolean setting_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets); +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[]; +extern NmcOutputField nmc_fields_setting_tun[]; +extern NmcOutputField nmc_fields_setting_ip_tunnel[]; +extern NmcOutputField nmc_fields_setting_macvlan[]; +extern NmcOutputField nmc_fields_setting_vxlan[]; + #endif /* NMC_SETTINGS_H */ diff --git a/clients/cli/utils.c b/clients/cli/utils.c index a406eee4..ff98276a 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -17,8 +17,6 @@ * Copyright 2010 - 2015 Red Hat, Inc. */ -/* Generated configuration file */ - #include "nm-default.h" #include <stdio.h> @@ -1005,7 +1003,7 @@ colorize_string (NmCli *nmc, if ( use_colors (nmc) && (color != NMC_TERM_COLOR_NORMAL || color_fmt != NMC_TERM_FORMAT_NORMAL)) { - out = nmc_colorize (nmc, color, color_fmt, str); + out = nmc_colorize (nmc, color, color_fmt, "%s", str); *dealloc = TRUE; } else { out = (char *) str; diff --git a/clients/cli/utils.h b/clients/cli/utils.h index eab60d3a..5adbd2be 100644 --- a/clients/cli/utils.h +++ b/clients/cli/utils.h @@ -74,7 +74,7 @@ void nmc_terminal_show_progress (const char *str); const char *nmc_term_color_sequence (NmcTermColor color); const char *nmc_term_format_sequence (NmcTermFormat format); NmcTermColor nmc_term_color_parse_string (const char *str, GError **error); -char *nmc_colorize (NmCli *nmc, NmcTermColor color, NmcTermFormat format, const char * fmt, ...); +char *nmc_colorize (NmCli *nmc, NmcTermColor color, NmcTermFormat format, const char * fmt, ...) _nm_printf (4, 5); void nmc_filter_out_colors_inplace (char *str); char *nmc_filter_out_colors (const char *str); char *nmc_get_user_input (const char *ask_str); diff --git a/clients/common/nm-polkit-listener.h b/clients/common/nm-polkit-listener.h index 22c436c1..a8256460 100644 --- a/clients/common/nm-polkit-listener.h +++ b/clients/common/nm-polkit-listener.h @@ -22,10 +22,6 @@ #define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE #include <polkitagent/polkitagent.h> -#include "nm-default.h" - -G_BEGIN_DECLS - #define NM_TYPE_POLKIT_LISTENER (nm_polkit_listener_get_type ()) #define NM_POLKIT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_POLKIT_LISTENER, NMPolkitListener)) #define NM_POLKIT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_POLKIT_LISTENER, NMPolkitListenerClass)) @@ -99,6 +95,4 @@ void nm_polkit_listener_set_show_error_callback (NMPolkitListener *self, void nm_polkit_listener_set_completed_callback (NMPolkitListener *self, NMPolkitListenerOnCompletedFunc completed_callback); -G_END_DECLS - #endif /* __NM_POLKIT_LISTENER_H__ */ diff --git a/clients/common/nm-secret-agent-simple.h b/clients/common/nm-secret-agent-simple.h index d2c58822..ba819ae3 100644 --- a/clients/common/nm-secret-agent-simple.h +++ b/clients/common/nm-secret-agent-simple.h @@ -22,8 +22,6 @@ #include <NetworkManager.h> #include <nm-secret-agent-old.h> -G_BEGIN_DECLS - #define NM_TYPE_SECRET_AGENT_SIMPLE (nm_secret_agent_simple_get_type ()) #define NM_SECRET_AGENT_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimple)) #define NM_SECRET_AGENT_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimpleClass)) @@ -59,6 +57,4 @@ void nm_secret_agent_simple_response (NMSecretAgentSimpl void nm_secret_agent_simple_enable (NMSecretAgentSimple *self, const char *path); -G_END_DECLS - #endif /* __NM_SECRET_AGENT_SIMPLE_H__ */ diff --git a/clients/common/nm-vpn-helpers.c b/clients/common/nm-vpn-helpers.c index fe777ef5..f6f12176 100644 --- a/clients/common/nm-vpn-helpers.c +++ b/clients/common/nm-vpn-helpers.c @@ -19,8 +19,6 @@ /** * SECTION:nm-vpn-helpers * @short_description: VPN-related utilities - * - * Some functions should probably eventually move into libnm. */ #include "nm-default.h" @@ -28,34 +26,26 @@ #include "nm-vpn-helpers.h" #include <string.h> -#include <gmodule.h> #include "nm-utils.h" -static gboolean plugins_loaded; -static GSList *plugins = NULL; +/*****************************************************************************/ NMVpnEditorPlugin * -nm_vpn_lookup_plugin (const char *name, const char *service, GError **error) +nm_vpn_get_editor_plugin (const char *service_type, GError **error) { NMVpnEditorPlugin *plugin = NULL; NMVpnPluginInfo *plugin_info; gs_free_error GError *local = NULL; - g_return_val_if_fail (!service ^ !name, NULL); + g_return_val_if_fail (service_type, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - if (G_UNLIKELY (!plugins_loaded)) - nm_vpn_get_plugins (); - - if (service) - plugin_info = nm_vpn_plugin_info_list_find_by_service (plugins, service); - else - plugin_info = nm_vpn_plugin_info_list_find_by_name (plugins, name); + plugin_info = nm_vpn_plugin_info_list_find_by_service (nm_vpn_get_plugin_infos (), service_type); if (!plugin_info) { g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_FAILED, - _("unknown VPN plugin \"%s\""), service ?: name); + _("unknown VPN plugin \"%s\""), service_type); return NULL; } plugin = nm_vpn_plugin_info_get_editor_plugin (plugin_info); @@ -87,8 +77,11 @@ nm_vpn_lookup_plugin (const char *name, const char *service, GError **error) } GSList * -nm_vpn_get_plugins (void) +nm_vpn_get_plugin_infos (void) { + static bool plugins_loaded; + static GSList *plugins = NULL; + if (G_LIKELY (plugins_loaded)) return plugins; plugins_loaded = TRUE; @@ -96,80 +89,6 @@ nm_vpn_get_plugins (void) return plugins; } -static int -_strcmp_data (gconstpointer a, gconstpointer b, gpointer unused) -{ - return strcmp (a, b); -} - -const char ** -nm_vpn_get_plugin_names (gboolean only_available_plugins) -{ - GSList *p; - const char **list; - const char *known_names[] = { - "openvpn", - "vpnc", - "pptp", - "openconnect", - "openswan", - "libreswan", - "strongswan", - "ssh", - "l2tp", - "iodine", - "fortisslvpn", - }; - guint i, j, k; - - p = nm_vpn_get_plugins (); - list = g_new0 (const char *, g_slist_length (p) + G_N_ELEMENTS (known_names) + 1); - - i = 0; - for (i = 0; p; p = p->next) - list[i++] = nm_vpn_plugin_info_get_name (p->data); - if (!only_available_plugins) { - for (j = 0; j < G_N_ELEMENTS (known_names); j++) - list[i++] = known_names[j]; - } - - g_qsort_with_data (list, i, sizeof (gpointer), _strcmp_data, NULL); - - /* remove duplicates */ - for (k = 0, j = 1; j < i; j++) { - if (nm_streq (list[k], list[j])) - continue; - list[k++] = list[j]; - } - list[k++] = NULL; - - return list; -} - -const char * -nm_vpn_get_service_for_name (const char *name) -{ - NMVpnPluginInfo *plugin_info; - - g_return_val_if_fail (name, NULL); - - plugin_info = nm_vpn_plugin_info_list_find_by_name (nm_vpn_get_plugins (), name); - if (plugin_info) { - /* this only means we have a .name file (NMVpnPluginInfo). Possibly the - * NMVpnEditorPlugin is not loadable. */ - return nm_vpn_plugin_info_lookup_property (plugin_info, - NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, - "service"); - } - return NULL; -} - -char * -nm_vpn_get_service_for_name_default (const char *name) -{ - return g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, name); -} - gboolean nm_vpn_supports_ipv6 (NMConnection *connection) { @@ -185,7 +104,7 @@ nm_vpn_supports_ipv6 (NMConnection *connection) if (!service_type) return FALSE; - plugin = nm_vpn_lookup_plugin (NULL, service_type, NULL); + plugin = nm_vpn_get_editor_plugin (service_type, NULL); if (!plugin) return FALSE; @@ -194,7 +113,7 @@ nm_vpn_supports_ipv6 (NMConnection *connection) } const VpnPasswordName * -nm_vpn_get_secret_names (const char *vpn_type) +nm_vpn_get_secret_names (const char *service_type) { const char *type; static VpnPasswordName generic_vpn_secrets[] = { {"password", N_("Password")}, {NULL, NULL} }; @@ -213,14 +132,16 @@ nm_vpn_get_secret_names (const char *vpn_type) {"gwcert", N_("Gateway certificate hash")}, {NULL, NULL} }; - if (!vpn_type) + if (!service_type) return NULL; - if (g_str_has_prefix (vpn_type, NM_DBUS_INTERFACE)) - type = vpn_type + strlen (NM_DBUS_INTERFACE) + 1; - else - type = vpn_type; + if ( !g_str_has_prefix (service_type, NM_DBUS_INTERFACE) + || service_type[NM_STRLEN (NM_DBUS_INTERFACE)] != '.') { + /* all our well-known, hard-coded vpn-types start with NM_DBUS_INTERFACE. */ + return NULL; + } + type = service_type + (NM_STRLEN (NM_DBUS_INTERFACE) + 1); if ( !g_strcmp0 (type, "pptp") || !g_strcmp0 (type, "iodine") || !g_strcmp0 (type, "ssh") diff --git a/clients/common/nm-vpn-helpers.h b/clients/common/nm-vpn-helpers.h index 79fc94e9..9c23f37c 100644 --- a/clients/common/nm-vpn-helpers.h +++ b/clients/common/nm-vpn-helpers.h @@ -21,25 +21,18 @@ #include <NetworkManager.h> -#include "nm-default.h" - struct { const char *name; const char *ui_name; } typedef VpnPasswordName; -GSList *nm_vpn_get_plugins (void); - -const char **nm_vpn_get_plugin_names (gboolean only_available_plugins); - -const char *nm_vpn_get_service_for_name (const char *name); -char * nm_vpn_get_service_for_name_default (const char *name); +GSList *nm_vpn_get_plugin_infos (void); -NMVpnEditorPlugin *nm_vpn_lookup_plugin (const char *name, const char *service, GError **error); +NMVpnEditorPlugin *nm_vpn_get_editor_plugin (const char *service_type, GError **error); gboolean nm_vpn_supports_ipv6 (NMConnection *connection); -const VpnPasswordName * nm_vpn_get_secret_names (const char *vpn_type); +const VpnPasswordName * nm_vpn_get_secret_names (const char *service_type); gboolean nm_vpn_openconnect_authenticate_helper (const char *host, char **cookie, diff --git a/clients/tui/Makefile.in b/clients/tui/Makefile.in index d7773283..0aaf339a 100644 --- a/clients/tui/Makefile.in +++ b/clients/tui/Makefile.in @@ -91,8 +91,9 @@ host_triplet = @host@ @BUILD_NMTUI_TRUE@bin_PROGRAMS = nmtui$(EXEEXT) subdir = clients/tui ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ - $(top_srcdir)/m4/compiler_warnings.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \ + $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_options.m4 \ $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gnome-code-coverage.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ @@ -330,6 +331,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@ DHCPCD_PATH = @DHCPCD_PATH@ DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ DLLTOOL = @DLLTOOL@ +DL_LIBS = @DL_LIBS@ DNSMASQ_PATH = @DNSMASQ_PATH@ DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@ DSYMUTIL = @DSYMUTIL@ @@ -386,13 +388,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ IPTABLES_PATH = @IPTABLES_PATH@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@ LIBAUDIT_LIBS = @LIBAUDIT_LIBS@ -LIBDL = @LIBDL@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ @@ -404,6 +407,8 @@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@ +LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@ LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ @@ -464,6 +469,8 @@ QT_CFLAGS = @QT_CFLAGS@ QT_LIBS = @QT_LIBS@ RANLIB = @RANLIB@ READLINE_LIBS = @READLINE_LIBS@ +SANITIZERS = @SANITIZERS@ +SANITIZER_ENV = @SANITIZER_ENV@ SED = @SED@ SELINUX_CFLAGS = @SELINUX_CFLAGS@ SELINUX_LIBS = @SELINUX_LIBS@ @@ -499,6 +506,7 @@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_RANLIB = @ac_ct_RANLIB@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ diff --git a/clients/tui/newt/Makefile.in b/clients/tui/newt/Makefile.in index 3042089f..12583516 100644 --- a/clients/tui/newt/Makefile.in +++ b/clients/tui/newt/Makefile.in @@ -90,8 +90,9 @@ build_triplet = @build@ host_triplet = @host@ subdir = clients/tui/newt ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ - $(top_srcdir)/m4/compiler_warnings.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \ + $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_options.m4 \ $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gnome-code-coverage.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ @@ -231,6 +232,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@ DHCPCD_PATH = @DHCPCD_PATH@ DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ DLLTOOL = @DLLTOOL@ +DL_LIBS = @DL_LIBS@ DNSMASQ_PATH = @DNSMASQ_PATH@ DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@ DSYMUTIL = @DSYMUTIL@ @@ -287,13 +289,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ IPTABLES_PATH = @IPTABLES_PATH@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@ LIBAUDIT_LIBS = @LIBAUDIT_LIBS@ -LIBDL = @LIBDL@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ @@ -305,6 +308,8 @@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@ +LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@ LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ @@ -365,6 +370,8 @@ QT_CFLAGS = @QT_CFLAGS@ QT_LIBS = @QT_LIBS@ RANLIB = @RANLIB@ READLINE_LIBS = @READLINE_LIBS@ +SANITIZERS = @SANITIZERS@ +SANITIZER_ENV = @SANITIZER_ENV@ SED = @SED@ SELINUX_CFLAGS = @SELINUX_CFLAGS@ SELINUX_LIBS = @SELINUX_LIBS@ @@ -400,6 +407,7 @@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_RANLIB = @ac_ct_RANLIB@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ diff --git a/clients/tui/newt/nmt-newt-button-box.h b/clients/tui/newt/nmt-newt-button-box.h index 970588a4..dec2c347 100644 --- a/clients/tui/newt/nmt-newt-button-box.h +++ b/clients/tui/newt/nmt-newt-button-box.h @@ -21,8 +21,6 @@ #include "nmt-newt-grid.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_BUTTON_BOX (nmt_newt_button_box_get_type ()) #define NMT_NEWT_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBox)) #define NMT_NEWT_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxClass)) @@ -60,6 +58,4 @@ void nmt_newt_button_box_add_widget_end (NmtNewtButtonBox *bbox, NmtNewtWidget *widget); -G_END_DECLS - #endif /* NMT_NEWT_BUTTON_BOX_H */ diff --git a/clients/tui/newt/nmt-newt-button.h b/clients/tui/newt/nmt-newt-button.h index 52e95c5d..646d0053 100644 --- a/clients/tui/newt/nmt-newt-button.h +++ b/clients/tui/newt/nmt-newt-button.h @@ -21,8 +21,6 @@ #include "nmt-newt-component.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_BUTTON (nmt_newt_button_get_type ()) #define NMT_NEWT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_BUTTON, NmtNewtButton)) #define NMT_NEWT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonClass)) @@ -48,6 +46,4 @@ void nmt_newt_button_set_label (NmtNewtButton *button, const char *label); const char *nmt_newt_button_get_label (NmtNewtButton *button); -G_END_DECLS - #endif /* NMT_NEWT_BUTTON_H */ diff --git a/clients/tui/newt/nmt-newt-checkbox.h b/clients/tui/newt/nmt-newt-checkbox.h index c277386b..e46e3ec2 100644 --- a/clients/tui/newt/nmt-newt-checkbox.h +++ b/clients/tui/newt/nmt-newt-checkbox.h @@ -21,8 +21,6 @@ #include "nmt-newt-component.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_CHECKBOX (nmt_newt_checkbox_get_type ()) #define NMT_NEWT_CHECKBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckbox)) #define NMT_NEWT_CHECKBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxClass)) @@ -48,6 +46,4 @@ void nmt_newt_checkbox_set_active (NmtNewtCheckbox *checkbox, gboolean active); gboolean nmt_newt_checkbox_get_active (NmtNewtCheckbox *checkbox); -G_END_DECLS - #endif /* NMT_NEWT_CHECKBOX_H */ diff --git a/clients/tui/newt/nmt-newt-component.h b/clients/tui/newt/nmt-newt-component.h index 2ffa018e..bf8f2bb8 100644 --- a/clients/tui/newt/nmt-newt-component.h +++ b/clients/tui/newt/nmt-newt-component.h @@ -21,8 +21,6 @@ #include "nmt-newt-widget.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_COMPONENT (nmt_newt_component_get_type ()) #define NMT_NEWT_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponent)) #define NMT_NEWT_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentClass)) @@ -52,6 +50,4 @@ gboolean nmt_newt_component_get_sensitive (NmtNewtComponent *component); void nmt_newt_component_set_sensitive (NmtNewtComponent *component, gboolean sensitive); -G_END_DECLS - #endif /* NMT_NEWT_COMPONENT_H */ diff --git a/clients/tui/newt/nmt-newt-container.h b/clients/tui/newt/nmt-newt-container.h index 6f182925..32d6abcc 100644 --- a/clients/tui/newt/nmt-newt-container.h +++ b/clients/tui/newt/nmt-newt-container.h @@ -21,8 +21,6 @@ #include "nmt-newt-widget.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_CONTAINER (nmt_newt_container_get_type ()) #define NMT_NEWT_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainer)) #define NMT_NEWT_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerClass)) @@ -56,6 +54,4 @@ void nmt_newt_container_remove (NmtNewtContainer *container, GSList *nmt_newt_container_get_children (NmtNewtContainer *container); -G_END_DECLS - #endif /* NMT_NEWT_CONTAINER_H */ diff --git a/clients/tui/newt/nmt-newt-entry-numeric.h b/clients/tui/newt/nmt-newt-entry-numeric.h index 43ac3449..00e19739 100644 --- a/clients/tui/newt/nmt-newt-entry-numeric.h +++ b/clients/tui/newt/nmt-newt-entry-numeric.h @@ -21,8 +21,6 @@ #include "nmt-newt-entry.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_ENTRY_NUMERIC (nmt_newt_entry_numeric_get_type ()) #define NMT_NEWT_ENTRY_NUMERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumeric)) #define NMT_NEWT_ENTRY_NUMERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericClass)) @@ -46,6 +44,4 @@ NmtNewtWidget *nmt_newt_entry_numeric_new (int width, int min, int max); -G_END_DECLS - #endif /* NMT_NEWT_ENTRY_NUMERIC_H */ diff --git a/clients/tui/newt/nmt-newt-entry.h b/clients/tui/newt/nmt-newt-entry.h index 8df0b137..36bd9d51 100644 --- a/clients/tui/newt/nmt-newt-entry.h +++ b/clients/tui/newt/nmt-newt-entry.h @@ -21,8 +21,6 @@ #include "nmt-newt-component.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_ENTRY (nmt_newt_entry_get_type ()) #define NMT_NEWT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_ENTRY, NmtNewtEntry)) #define NMT_NEWT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryClass)) @@ -69,6 +67,4 @@ void nmt_newt_entry_set_width (NmtNewtEntry *entry, int width); int nmt_newt_entry_get_width (NmtNewtEntry *entry); -G_END_DECLS - #endif /* NMT_NEWT_ENTRY_H */ diff --git a/clients/tui/newt/nmt-newt-form.h b/clients/tui/newt/nmt-newt-form.h index be95eb68..2cae5b5e 100644 --- a/clients/tui/newt/nmt-newt-form.h +++ b/clients/tui/newt/nmt-newt-form.h @@ -21,8 +21,6 @@ #include "nmt-newt-container.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_FORM (nmt_newt_form_get_type ()) #define NMT_NEWT_FORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_FORM, NmtNewtForm)) #define NMT_NEWT_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_FORM, NmtNewtFormClass)) @@ -61,6 +59,4 @@ void nmt_newt_form_quit (NmtNewtForm *form); void nmt_newt_form_set_focus (NmtNewtForm *form, NmtNewtWidget *widget); -G_END_DECLS - #endif /* NMT_NEWT_FORM_H */ diff --git a/clients/tui/newt/nmt-newt-grid.h b/clients/tui/newt/nmt-newt-grid.h index f36a38ee..11bfdec3 100644 --- a/clients/tui/newt/nmt-newt-grid.h +++ b/clients/tui/newt/nmt-newt-grid.h @@ -21,8 +21,6 @@ #include "nmt-newt-container.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_GRID (nmt_newt_grid_get_type ()) #define NMT_NEWT_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_GRID, NmtNewtGrid)) #define NMT_NEWT_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_GRID, NmtNewtGridClass)) @@ -67,6 +65,4 @@ void nmt_newt_grid_set_flags (NmtNewtGrid *grid, NmtNewtWidget *widget, NmtNewtGridFlags flags); -G_END_DECLS - #endif /* NMT_NEWT_GRID_H */ diff --git a/clients/tui/newt/nmt-newt-label.h b/clients/tui/newt/nmt-newt-label.h index a4e0dcca..ef894d91 100644 --- a/clients/tui/newt/nmt-newt-label.h +++ b/clients/tui/newt/nmt-newt-label.h @@ -21,8 +21,6 @@ #include "nmt-newt-component.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_LABEL (nmt_newt_label_get_type ()) #define NMT_NEWT_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_LABEL, NmtNewtLabel)) #define NMT_NEWT_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_LABEL, NmtNewtLabelClass)) @@ -61,6 +59,4 @@ void nmt_newt_label_set_highlight (NmtNewtLabel *label, gboolean highlight); gboolean nmt_newt_label_get_highlight (NmtNewtLabel *label); -G_END_DECLS - #endif /* NMT_NEWT_LABEL_H */ diff --git a/clients/tui/newt/nmt-newt-listbox.h b/clients/tui/newt/nmt-newt-listbox.h index c18c9f8e..eba17a7c 100644 --- a/clients/tui/newt/nmt-newt-listbox.h +++ b/clients/tui/newt/nmt-newt-listbox.h @@ -21,8 +21,6 @@ #include "nmt-newt-component.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_LISTBOX (nmt_newt_listbox_get_type ()) #define NMT_NEWT_LISTBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListbox)) #define NMT_NEWT_LISTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxClass)) @@ -66,6 +64,4 @@ void nmt_newt_listbox_set_active_key (NmtNewtListbox *listbox, int nmt_newt_listbox_get_active (NmtNewtListbox *listbox); gpointer nmt_newt_listbox_get_active_key (NmtNewtListbox *listbox); -G_END_DECLS - #endif /* NMT_NEWT_LISTBOX_H */ diff --git a/clients/tui/newt/nmt-newt-popup.h b/clients/tui/newt/nmt-newt-popup.h index 643ea2d8..be2bc34c 100644 --- a/clients/tui/newt/nmt-newt-popup.h +++ b/clients/tui/newt/nmt-newt-popup.h @@ -21,8 +21,6 @@ #include "nmt-newt-button.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_POPUP (nmt_newt_popup_get_type ()) #define NMT_NEWT_POPUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_POPUP, NmtNewtPopup)) #define NMT_NEWT_POPUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_POPUP, NmtNewtPopupClass)) @@ -57,6 +55,4 @@ const char *nmt_newt_popup_get_active_id (NmtNewtPopup *popup); void nmt_newt_popup_set_active_id (NmtNewtPopup *popup, const char *active_id); -G_END_DECLS - #endif /* NMT_NEWT_POPUP_H */ diff --git a/clients/tui/newt/nmt-newt-section.h b/clients/tui/newt/nmt-newt-section.h index 6bf59ebb..569e86e5 100644 --- a/clients/tui/newt/nmt-newt-section.h +++ b/clients/tui/newt/nmt-newt-section.h @@ -21,8 +21,6 @@ #include "nmt-newt-container.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_SECTION (nmt_newt_section_get_type ()) #define NMT_NEWT_SECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSection)) #define NMT_NEWT_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_SECTION, NmtNewtSectionClass)) @@ -52,6 +50,4 @@ void nmt_newt_section_set_body (NmtNewtSection *section, NmtNewtWidget *body); NmtNewtWidget *nmt_newt_section_get_body (NmtNewtSection *section); -G_END_DECLS - #endif /* NMT_NEWT_SECTION_H */ diff --git a/clients/tui/newt/nmt-newt-separator.h b/clients/tui/newt/nmt-newt-separator.h index 3f4183d5..71503099 100644 --- a/clients/tui/newt/nmt-newt-separator.h +++ b/clients/tui/newt/nmt-newt-separator.h @@ -21,8 +21,6 @@ #include "nmt-newt-component.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_SEPARATOR (nmt_newt_separator_get_type ()) #define NMT_NEWT_SEPARATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparator)) #define NMT_NEWT_SEPARATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparatorClass)) @@ -44,6 +42,4 @@ GType nmt_newt_separator_get_type (void); NmtNewtWidget *nmt_newt_separator_new (void); -G_END_DECLS - #endif /* NMT_NEWT_SEPARATOR_H */ diff --git a/clients/tui/newt/nmt-newt-stack.h b/clients/tui/newt/nmt-newt-stack.h index 09afe4ba..c9bb5a07 100644 --- a/clients/tui/newt/nmt-newt-stack.h +++ b/clients/tui/newt/nmt-newt-stack.h @@ -21,8 +21,6 @@ #include "nmt-newt-container.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_STACK (nmt_newt_stack_get_type ()) #define NMT_NEWT_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_STACK, NmtNewtStack)) #define NMT_NEWT_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_STACK, NmtNewtStackClass)) @@ -56,6 +54,4 @@ void nmt_newt_stack_set_active_id (NmtNewtStack *stack, const char *id); const char * nmt_newt_stack_get_active_id (NmtNewtStack *stack); -G_END_DECLS - #endif /* NMT_NEWT_STACK_H */ diff --git a/clients/tui/newt/nmt-newt-textbox.h b/clients/tui/newt/nmt-newt-textbox.h index b3743aea..31a159e2 100644 --- a/clients/tui/newt/nmt-newt-textbox.h +++ b/clients/tui/newt/nmt-newt-textbox.h @@ -21,8 +21,6 @@ #include "nmt-newt-component.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_TEXTBOX (nmt_newt_textbox_get_type ()) #define NMT_NEWT_TEXTBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextbox)) #define NMT_NEWT_TEXTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxClass)) @@ -54,6 +52,4 @@ void nmt_newt_textbox_set_text (NmtNewtTextbox *textbox, const char *text); const char *nmt_newt_textbox_get_text (NmtNewtTextbox *textbox); -G_END_DECLS - #endif /* NMT_NEWT_TEXTBOX_H */ diff --git a/clients/tui/newt/nmt-newt-toggle-button.h b/clients/tui/newt/nmt-newt-toggle-button.h index e54c6d3f..69c0b7d1 100644 --- a/clients/tui/newt/nmt-newt-toggle-button.h +++ b/clients/tui/newt/nmt-newt-toggle-button.h @@ -21,8 +21,6 @@ #include "nmt-newt-button.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_TOGGLE_BUTTON (nmt_newt_toggle_button_get_type ()) #define NMT_NEWT_TOGGLE_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButton)) #define NMT_NEWT_TOGGLE_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonClass)) @@ -49,6 +47,4 @@ gboolean nmt_newt_toggle_button_get_active (NmtNewtToggleButton *button); void nmt_newt_toggle_button_set_active (NmtNewtToggleButton *button, gboolean active); -G_END_DECLS - #endif /* NMT_NEWT_TOGGLE_BUTTON_H */ diff --git a/clients/tui/newt/nmt-newt-types.h b/clients/tui/newt/nmt-newt-types.h index 4ea0a7b8..af550ca0 100644 --- a/clients/tui/newt/nmt-newt-types.h +++ b/clients/tui/newt/nmt-newt-types.h @@ -21,10 +21,6 @@ #include <newt.h> -#include "nm-default.h" - -G_BEGIN_DECLS - typedef struct _NmtNewtButton NmtNewtButton; typedef struct _NmtNewtButtonBox NmtNewtButtonBox; typedef struct _NmtNewtCheckbox NmtNewtCheckbox; @@ -45,6 +41,4 @@ typedef struct _NmtNewtTextbox NmtNewtTextbox; typedef struct _NmtNewtToggleButton NmtNewtToggleButton; typedef struct _NmtNewtWidget NmtNewtWidget; -G_END_DECLS - #endif /* NMT_NEWT_COMPONENT_H */ diff --git a/clients/tui/newt/nmt-newt-utils.h b/clients/tui/newt/nmt-newt-utils.h index 706f10d3..49d717d2 100644 --- a/clients/tui/newt/nmt-newt-utils.h +++ b/clients/tui/newt/nmt-newt-utils.h @@ -21,10 +21,6 @@ #include <newt.h> -#include "nm-default.h" - -G_BEGIN_DECLS - void nmt_newt_init (void); void nmt_newt_finished (void); @@ -41,14 +37,12 @@ char *nmt_newt_locale_from_utf8 (const char *str_utf8); int nmt_newt_text_width (const char *str); void nmt_newt_message_dialog (const char *message, - ...); + ...) _nm_printf (1, 2); int nmt_newt_choice_dialog (const char *button1, const char *button2, const char *message, - ...); + ...) _nm_printf (3, 4); char *nmt_newt_edit_string (const char *data); -G_END_DECLS - #endif /* NMT_NEWT_UTILS_H */ diff --git a/clients/tui/newt/nmt-newt-widget.h b/clients/tui/newt/nmt-newt-widget.h index a526a674..34ce943c 100644 --- a/clients/tui/newt/nmt-newt-widget.h +++ b/clients/tui/newt/nmt-newt-widget.h @@ -21,8 +21,6 @@ #include "nmt-newt-types.h" -G_BEGIN_DECLS - #define NMT_TYPE_NEWT_WIDGET (nmt_newt_widget_get_type ()) #define NMT_NEWT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidget)) #define NMT_NEWT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetClass)) @@ -113,6 +111,4 @@ void nmt_newt_widget_needs_rebuild (NmtNewtWidget *widget); void nmt_newt_widget_set_valid (NmtNewtWidget *widget, gboolean valid); -G_END_DECLS - #endif /* NMT_NEWT_WIDGET_H */ diff --git a/clients/tui/nm-editor-bindings.h b/clients/tui/nm-editor-bindings.h index c4ed95d5..fd4fcd30 100644 --- a/clients/tui/nm-editor-bindings.h +++ b/clients/tui/nm-editor-bindings.h @@ -21,8 +21,6 @@ #include <NetworkManager.h> -G_BEGIN_DECLS - void nm_editor_bindings_init (void); void nm_editor_bind_ip_addresses_with_prefix_to_strv (int family, @@ -71,6 +69,4 @@ void nm_editor_bind_wireless_security_wep_key (NMSettingWirelessSecurity void nm_editor_bind_vlan_name (NMSettingVlan *s_vlan, NMSettingConnection *s_con); -G_END_DECLS - #endif /* NM_EDITOR_BINDINGS_H */ diff --git a/clients/tui/nm-editor-utils.c b/clients/tui/nm-editor-utils.c index e582d41f..0ec55f3d 100644 --- a/clients/tui/nm-editor-utils.c +++ b/clients/tui/nm-editor-utils.c @@ -223,7 +223,7 @@ nm_editor_utils_get_connection_type_list (void) #if 0 /* Add "VPN" only if there are plugins */ - vpn_plugins_hash = nm_vpn_get_plugins (); + vpn_plugins_hash = nm_vpn_get_plugin_infos (); have_vpn_plugins = vpn_plugins_hash && g_hash_table_size (vpn_plugins_hash); if (have_vpn_plugins) { GHashTableIter iter; @@ -252,6 +252,18 @@ nm_editor_utils_get_connection_type_list (void) return list; } +static gboolean +_assert_format_int (const char *format) +{ + g_assert (format); + format = strchr (format, '%'); + g_assert (format); + format++; + g_assert (!strchr (format, '%')); + g_assert (format[0] == 'd'); + return TRUE; +} + static char * get_available_connection_name (const char *format, NMClient *client) @@ -261,6 +273,8 @@ get_available_connection_name (const char *format, char *cname = NULL; int i = 0; + nm_assert (_assert_format_int (format)); + conns = nm_client_get_connections (client); for (i = 0; i < conns->len; i++) { const char *id; @@ -275,7 +289,9 @@ get_available_connection_name (const char *format, char *temp; gboolean found = FALSE; + NM_PRAGMA_WARNING_DISABLE("-Wformat-nonliteral") temp = g_strdup_printf (format, i); + NM_PRAGMA_WARNING_REENABLE for (iter = names; iter; iter = g_slist_next (iter)) { if (!strcmp (iter->data, temp)) { found = TRUE; diff --git a/clients/tui/nm-editor-utils.h b/clients/tui/nm-editor-utils.h index 5e402ce7..15a16cd2 100644 --- a/clients/tui/nm-editor-utils.h +++ b/clients/tui/nm-editor-utils.h @@ -21,8 +21,6 @@ #include <NetworkManager.h> -G_BEGIN_DECLS - typedef struct { const char *name; GType setting_type; @@ -38,6 +36,4 @@ NMConnection *nm_editor_utils_create_connection (GType type, NMConnection *master, NMClient *client); -G_END_DECLS - #endif /* NM_EDITOR_UTILS_H */ diff --git a/clients/tui/nmt-address-list.h b/clients/tui/nmt-address-list.h index df7a4f79..f82f2181 100644 --- a/clients/tui/nmt-address-list.h +++ b/clients/tui/nmt-address-list.h @@ -21,8 +21,6 @@ #include "nmt-widget-list.h" -G_BEGIN_DECLS - #define NMT_TYPE_ADDRESS_LIST (nmt_address_list_get_type ()) #define NMT_ADDRESS_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ADDRESS_LIST, NmtAddressList)) #define NMT_ADDRESS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ADDRESS_LIST, NmtAddressListClass)) @@ -52,6 +50,4 @@ typedef enum { NmtNewtWidget *nmt_address_list_new (NmtAddressListType list_type); -G_END_DECLS - #endif /* NMT_ADDRESS_LIST_H */ diff --git a/clients/tui/nmt-connect-connection-list.h b/clients/tui/nmt-connect-connection-list.h index d4790043..bf4721b9 100644 --- a/clients/tui/nmt-connect-connection-list.h +++ b/clients/tui/nmt-connect-connection-list.h @@ -21,8 +21,6 @@ #include "nmt-newt.h" -G_BEGIN_DECLS - #define NMT_TYPE_CONNECT_CONNECTION_LIST (nmt_connect_connection_list_get_type ()) #define NMT_CONNECT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionList)) #define NMT_CONNECT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListClass)) @@ -56,6 +54,4 @@ gboolean nmt_connect_connection_list_get_selection (NmtConnectConnectionList * NMObject **specific_object, NMActiveConnection **active); -G_END_DECLS - #endif /* NMT_CONNECT_CONNECTION_LIST_H */ diff --git a/clients/tui/nmt-device-entry.h b/clients/tui/nmt-device-entry.h index 05d5d40f..4945e7c2 100644 --- a/clients/tui/nmt-device-entry.h +++ b/clients/tui/nmt-device-entry.h @@ -23,8 +23,6 @@ #include <NetworkManager.h> -G_BEGIN_DECLS - #define NMT_TYPE_DEVICE_ENTRY (nmt_device_entry_get_type ()) #define NMT_DEVICE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntry)) #define NMT_DEVICE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryClass)) @@ -55,6 +53,4 @@ void nmt_device_entry_set_device_filter (NmtDeviceEntry *deventry, NmtDeviceEntryDeviceFilter filter, gpointer user_data); -G_END_DECLS - #endif /* NMT_DEVICE_ENTRY_H */ diff --git a/clients/tui/nmt-edit-connection-list.h b/clients/tui/nmt-edit-connection-list.h index c31c0a8c..fd492bce 100644 --- a/clients/tui/nmt-edit-connection-list.h +++ b/clients/tui/nmt-edit-connection-list.h @@ -23,8 +23,6 @@ #include <NetworkManager.h> -G_BEGIN_DECLS - #define NMT_TYPE_EDIT_CONNECTION_LIST (nmt_edit_connection_list_get_type ()) #define NMT_EDIT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionList)) #define NMT_EDIT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListClass)) @@ -54,6 +52,4 @@ typedef gboolean (*NmtEditConnectionListFilter) (NmtEditConnectionList *list, NMConnection *connection, gpointer user_data); -G_END_DECLS - #endif /* NMT_EDIT_CONNECTION_LIST_H */ diff --git a/clients/tui/nmt-editor-grid.h b/clients/tui/nmt-editor-grid.h index 2ae01d71..48676896 100644 --- a/clients/tui/nmt-editor-grid.h +++ b/clients/tui/nmt-editor-grid.h @@ -21,8 +21,6 @@ #include "nmt-newt.h" -G_BEGIN_DECLS - #define NMT_TYPE_EDITOR_GRID (nmt_editor_grid_get_type ()) #define NMT_EDITOR_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_GRID, NmtEditorGrid)) #define NMT_EDITOR_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_GRID, NmtEditorGridClass)) @@ -57,6 +55,4 @@ void nmt_editor_grid_set_row_flags (NmtEditorGrid *grid, NmtNewtWidget *widget, NmtEditorGridRowFlags flags); -G_END_DECLS - #endif /* NMT_EDITOR_GRID_H */ diff --git a/clients/tui/nmt-editor-page-device.h b/clients/tui/nmt-editor-page-device.h index f911602c..75aecd01 100644 --- a/clients/tui/nmt-editor-page-device.h +++ b/clients/tui/nmt-editor-page-device.h @@ -22,8 +22,6 @@ #include "nmt-editor-page.h" #include "nmt-device-entry.h" -G_BEGIN_DECLS - #define NMT_TYPE_EDITOR_PAGE_DEVICE (nmt_editor_page_device_get_type ()) #define NMT_EDITOR_PAGE_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDevice)) #define NMT_EDITOR_PAGE_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDeviceClass)) @@ -45,6 +43,4 @@ GType nmt_editor_page_device_get_type (void); NmtDeviceEntry *nmt_editor_page_device_get_device_entry (NmtEditorPageDevice *page); -G_END_DECLS - #endif /* NMT_EDITOR_PAGE_DEVICE_H */ diff --git a/clients/tui/nmt-editor-page.h b/clients/tui/nmt-editor-page.h index bbc0d9e3..8da5ea9f 100644 --- a/clients/tui/nmt-editor-page.h +++ b/clients/tui/nmt-editor-page.h @@ -24,8 +24,6 @@ #include "nmt-editor-grid.h" #include "nmt-editor-section.h" -G_BEGIN_DECLS - #define NMT_TYPE_EDITOR_PAGE (nmt_editor_page_get_type ()) #define NMT_EDITOR_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_PAGE, NmtEditorPage)) #define NMT_EDITOR_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_PAGE, NmtEditorPageClass)) @@ -53,6 +51,4 @@ GSList *nmt_editor_page_get_sections (NmtEditorPage *page); void nmt_editor_page_add_section (NmtEditorPage *page, NmtEditorSection *section); -G_END_DECLS - #endif /* NMT_EDITOR_PAGE_H */ diff --git a/clients/tui/nmt-editor-section.h b/clients/tui/nmt-editor-section.h index 67410c71..3c2537be 100644 --- a/clients/tui/nmt-editor-section.h +++ b/clients/tui/nmt-editor-section.h @@ -22,8 +22,6 @@ #include "nmt-newt-section.h" #include "nmt-editor-grid.h" -G_BEGIN_DECLS - #define NMT_TYPE_EDITOR_SECTION (nmt_editor_section_get_type ()) #define NMT_EDITOR_SECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_SECTION, NmtEditorSection)) #define NMT_EDITOR_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_SECTION, NmtEditorSectionClass)) @@ -51,6 +49,4 @@ const char *nmt_editor_section_get_title (NmtEditorSection *sectio NmtNewtWidget *nmt_editor_section_get_header_widget (NmtEditorSection *section); NmtEditorGrid *nmt_editor_section_get_body (NmtEditorSection *section); -G_END_DECLS - #endif /* NMT_EDITOR_SECTION_H */ diff --git a/clients/tui/nmt-editor.h b/clients/tui/nmt-editor.h index db10b82a..b000f64e 100644 --- a/clients/tui/nmt-editor.h +++ b/clients/tui/nmt-editor.h @@ -23,8 +23,6 @@ #include "nmt-newt.h" -G_BEGIN_DECLS - #define NMT_TYPE_EDITOR (nmt_editor_get_type ()) #define NMT_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR, NmtEditor)) #define NMT_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR, NmtEditorClass)) @@ -46,6 +44,4 @@ GType nmt_editor_get_type (void); NmtNewtForm *nmt_editor_new (NMConnection *connection); -G_END_DECLS - #endif /* NMT_EDITOR_H */ diff --git a/clients/tui/nmt-ip-entry.h b/clients/tui/nmt-ip-entry.h index ceb355ef..2338a873 100644 --- a/clients/tui/nmt-ip-entry.h +++ b/clients/tui/nmt-ip-entry.h @@ -21,8 +21,6 @@ #include "nmt-newt.h" -G_BEGIN_DECLS - #define NMT_TYPE_IP_ENTRY (nmt_ip_entry_get_type ()) #define NMT_IP_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_IP_ENTRY, NmtIPEntry)) #define NMT_IP_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_IP_ENTRY, NmtIPEntryClass)) @@ -47,6 +45,4 @@ NmtNewtWidget *nmt_ip_entry_new (int width, gboolean prefix, gboolean optional); -G_END_DECLS - #endif /* NMT_IP_ENTRY_H */ diff --git a/clients/tui/nmt-mac-entry.c b/clients/tui/nmt-mac-entry.c index d0f07e12..da7f55fa 100644 --- a/clients/tui/nmt-mac-entry.c +++ b/clients/tui/nmt-mac-entry.c @@ -28,11 +28,13 @@ #include "nm-default.h" +#include "nmt-mac-entry.h" + #include <string.h> #include "NetworkManager.h" +#include "nm-common-macros.h" -#include "nmt-mac-entry.h" G_DEFINE_TYPE (NmtMacEntry, nmt_mac_entry, NMT_TYPE_NEWT_ENTRY) @@ -41,6 +43,7 @@ G_DEFINE_TYPE (NmtMacEntry, nmt_mac_entry, NMT_TYPE_NEWT_ENTRY) typedef struct { int mac_length; int mac_str_length; + NmtMacEntryType entry_type; } NmtMacEntryPrivate; @@ -48,6 +51,7 @@ enum { PROP_0, PROP_MAC_LENGTH, PROP_MAC_ADDRESS, + PROP_ENTRY_TYPE, LAST_PROP }; @@ -57,6 +61,7 @@ enum { * @width: the width in characters of the entry * @mac_length: the length in bytes of the hardware address * (either %ETH_ALEN or %INFINIBAND_ALEN) + * @entry_type: the type of the entry. * * Creates a new #NmtMacEntry. * @@ -64,11 +69,13 @@ enum { */ NmtNewtWidget * nmt_mac_entry_new (int width, - int mac_length) + int mac_length, + NmtMacEntryType entry_type) { return g_object_new (NMT_TYPE_MAC_ENTRY, "width", width, "mac-length", mac_length, + "entry-type", (int) entry_type, NULL); } @@ -81,6 +88,9 @@ mac_filter (NmtNewtEntry *entry, { NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (entry); + if (priv->entry_type != NMT_MAC_ENTRY_TYPE_MAC) + return TRUE; + if (position >= priv->mac_str_length) return FALSE; @@ -98,6 +108,11 @@ mac_validator (NmtNewtEntry *entry, if (!*text) return TRUE; + if (priv->entry_type == NMT_MAC_ENTRY_TYPE_CLONED) { + if (NM_CLONED_MAC_IS_SPECIAL (text)) + return TRUE; + } + p = text; while ( g_ascii_isxdigit (p[0]) && g_ascii_isxdigit (p[1]) @@ -112,7 +127,9 @@ mac_validator (NmtNewtEntry *entry, if (!*p) return (p - text == priv->mac_str_length); - if (g_ascii_isxdigit (p[0]) && !p[1]) { + if ( g_ascii_isxdigit (p[0]) + && !p[1] + && p - text < priv->mac_str_length) { char *fixed = g_strdup_printf ("%.*s:%c", (int)(p - text), text, *p); nmt_newt_entry_set_text (entry, fixed); @@ -161,6 +178,10 @@ nmt_mac_entry_set_property (GObject *object, case PROP_MAC_ADDRESS: nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), g_value_get_string (value)); break; + case PROP_ENTRY_TYPE: + /* construct-only */ + priv->entry_type = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -182,6 +203,9 @@ nmt_mac_entry_get_property (GObject *object, case PROP_MAC_ADDRESS: g_value_set_string (value, nmt_newt_entry_get_text (NMT_NEWT_ENTRY (object))); break; + case PROP_ENTRY_TYPE: + g_value_set_int (value, priv->entry_type); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -224,4 +248,17 @@ nmt_mac_entry_class_init (NmtMacEntryClass *entry_class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtMacEntry:entry-type: + * + * The type of the #NmtMacEntry. Can be either used for plain + * MAC addresses or for the extended format for cloned MAC addresses. + */ + g_object_class_install_property + (object_class, PROP_ENTRY_TYPE, + g_param_spec_int ("entry-type", "", "", + NMT_MAC_ENTRY_TYPE_MAC, NMT_MAC_ENTRY_TYPE_CLONED, NMT_MAC_ENTRY_TYPE_MAC, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); } diff --git a/clients/tui/nmt-mac-entry.h b/clients/tui/nmt-mac-entry.h index 099bacb7..68d9a208 100644 --- a/clients/tui/nmt-mac-entry.h +++ b/clients/tui/nmt-mac-entry.h @@ -19,11 +19,13 @@ #ifndef NMT_MAC_ENTRY_H #define NMT_MAC_ENTRY_H -#include "nm-default.h" #include "nm-utils.h" #include "nmt-newt.h" -G_BEGIN_DECLS +typedef enum { /*< skip >*/ + NMT_MAC_ENTRY_TYPE_MAC, + NMT_MAC_ENTRY_TYPE_CLONED, +} NmtMacEntryType; #define NMT_TYPE_MAC_ENTRY (nmt_mac_entry_get_type ()) #define NMT_MAC_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntry)) @@ -45,8 +47,7 @@ typedef struct { GType nmt_mac_entry_get_type (void); NmtNewtWidget *nmt_mac_entry_new (int width, - int mac_length); - -G_END_DECLS + int mac_length, + NmtMacEntryType type); #endif /* NMT_MAC_ENTRY_H */ diff --git a/clients/tui/nmt-mtu-entry.h b/clients/tui/nmt-mtu-entry.h index dfe416d0..b7b2cf46 100644 --- a/clients/tui/nmt-mtu-entry.h +++ b/clients/tui/nmt-mtu-entry.h @@ -21,8 +21,6 @@ #include "nmt-newt.h" -G_BEGIN_DECLS - #define NMT_TYPE_MTU_ENTRY (nmt_mtu_entry_get_type ()) #define NMT_MTU_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_MTU_ENTRY, NmtMtuEntry)) #define NMT_MTU_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_MTU_ENTRY, NmtMtuEntryClass)) @@ -44,6 +42,4 @@ GType nmt_mtu_entry_get_type (void); NmtNewtWidget *nmt_mtu_entry_new (void); -G_END_DECLS - #endif /* NMT_MTU_ENTRY_H */ diff --git a/clients/tui/nmt-page-bond.h b/clients/tui/nmt-page-bond.h index b77477db..d9b071ef 100644 --- a/clients/tui/nmt-page-bond.h +++ b/clients/tui/nmt-page-bond.h @@ -21,8 +21,6 @@ #include "nmt-editor-page-device.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_BOND (nmt_page_bond_get_type ()) #define NMT_PAGE_BOND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BOND, NmtPageBond)) #define NMT_PAGE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BOND, NmtPageBondClass)) @@ -45,6 +43,4 @@ GType nmt_page_bond_get_type (void); NmtEditorPage *nmt_page_bond_new (NMConnection *conn, NmtDeviceEntry *deventry); -G_END_DECLS - #endif /* NMT_PAGE_BOND_H */ diff --git a/clients/tui/nmt-page-bridge-port.h b/clients/tui/nmt-page-bridge-port.h index c566dbc0..95689fff 100644 --- a/clients/tui/nmt-page-bridge-port.h +++ b/clients/tui/nmt-page-bridge-port.h @@ -21,8 +21,6 @@ #include "nmt-editor-page.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_BRIDGE_PORT (nmt_page_bridge_port_get_type ()) #define NMT_PAGE_BRIDGE_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePort)) #define NMT_PAGE_BRIDGE_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePortClass)) @@ -44,6 +42,4 @@ GType nmt_page_bridge_port_get_type (void); NmtEditorPage *nmt_page_bridge_port_new (NMConnection *conn); -G_END_DECLS - #endif /* NMT_PAGE_BRIDGE_PORT_H */ diff --git a/clients/tui/nmt-page-bridge.h b/clients/tui/nmt-page-bridge.h index 60cbdea2..21d850af 100644 --- a/clients/tui/nmt-page-bridge.h +++ b/clients/tui/nmt-page-bridge.h @@ -21,8 +21,6 @@ #include "nmt-editor-page-device.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_BRIDGE (nmt_page_bridge_get_type ()) #define NMT_PAGE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BRIDGE, NmtPageBridge)) #define NMT_PAGE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BRIDGE, NmtPageBridgeClass)) @@ -45,6 +43,4 @@ GType nmt_page_bridge_get_type (void); NmtEditorPage *nmt_page_bridge_new (NMConnection *conn, NmtDeviceEntry *deventry); -G_END_DECLS - #endif /* NMT_PAGE_BRIDGE_H */ diff --git a/clients/tui/nmt-page-dsl.h b/clients/tui/nmt-page-dsl.h index dc84ca07..aa436e71 100644 --- a/clients/tui/nmt-page-dsl.h +++ b/clients/tui/nmt-page-dsl.h @@ -19,11 +19,8 @@ #ifndef NMT_PAGE_DSL_H #define NMT_PAGE_DSL_H -#include "nm-default.h" #include "nmt-editor-page-device.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_DSL (nmt_page_dsl_get_type ()) #define NMT_PAGE_DSL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_DSL, NmtPageDsl)) #define NMT_PAGE_DSL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_DSL, NmtPageDslClass)) @@ -46,6 +43,4 @@ GType nmt_page_dsl_get_type (void); NmtEditorPage *nmt_page_dsl_new (NMConnection *conn, NmtDeviceEntry *deventry); -G_END_DECLS - #endif /* NMT_PAGE_DSL_H */ diff --git a/clients/tui/nmt-page-ethernet.c b/clients/tui/nmt-page-ethernet.c index dfe2e440..6b924327 100644 --- a/clients/tui/nmt-page-ethernet.c +++ b/clients/tui/nmt-page-ethernet.c @@ -70,7 +70,7 @@ nmt_page_ethernet_constructed (GObject *object) section = nmt_editor_section_new (_("ETHERNET"), NULL, FALSE); grid = nmt_editor_section_get_body (section); - widget = nmt_mac_entry_new (40, ETH_ALEN); + widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); diff --git a/clients/tui/nmt-page-ethernet.h b/clients/tui/nmt-page-ethernet.h index bdf3d677..56d917e1 100644 --- a/clients/tui/nmt-page-ethernet.h +++ b/clients/tui/nmt-page-ethernet.h @@ -21,8 +21,6 @@ #include "nmt-editor-page-device.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_ETHERNET (nmt_page_ethernet_get_type ()) #define NMT_PAGE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernet)) #define NMT_PAGE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernetClass)) @@ -45,6 +43,4 @@ GType nmt_page_ethernet_get_type (void); NmtEditorPage *nmt_page_ethernet_new (NMConnection *conn, NmtDeviceEntry *deventry); -G_END_DECLS - #endif /* NMT_PAGE_ETHERNET_H */ diff --git a/clients/tui/nmt-page-infiniband.h b/clients/tui/nmt-page-infiniband.h index f51e0874..e2e0fbbc 100644 --- a/clients/tui/nmt-page-infiniband.h +++ b/clients/tui/nmt-page-infiniband.h @@ -21,8 +21,6 @@ #include "nmt-editor-page-device.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_INFINIBAND (nmt_page_infiniband_get_type ()) #define NMT_PAGE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfiniband)) #define NMT_PAGE_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfinibandClass)) @@ -45,6 +43,4 @@ GType nmt_page_infiniband_get_type (void); NmtEditorPage *nmt_page_infiniband_new (NMConnection *conn, NmtDeviceEntry *deventry); -G_END_DECLS - #endif /* NMT_PAGE_INFINIBAND_H */ diff --git a/clients/tui/nmt-page-ip4.h b/clients/tui/nmt-page-ip4.h index e3c5a003..f120e57d 100644 --- a/clients/tui/nmt-page-ip4.h +++ b/clients/tui/nmt-page-ip4.h @@ -21,8 +21,6 @@ #include "nmt-editor-page.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_IP4 (nmt_page_ip4_get_type ()) #define NMT_PAGE_IP4(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_IP4, NmtPageIP4)) #define NMT_PAGE_IP4_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_IP4, NmtPageIP4Class)) @@ -44,6 +42,4 @@ GType nmt_page_ip4_get_type (void); NmtEditorPage *nmt_page_ip4_new (NMConnection *conn); -G_END_DECLS - #endif /* NMT_PAGE_IP4_H */ diff --git a/clients/tui/nmt-page-ip6.h b/clients/tui/nmt-page-ip6.h index 498261e1..3a471447 100644 --- a/clients/tui/nmt-page-ip6.h +++ b/clients/tui/nmt-page-ip6.h @@ -21,8 +21,6 @@ #include "nmt-editor-page.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_IP6 (nmt_page_ip6_get_type ()) #define NMT_PAGE_IP6(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_IP6, NmtPageIP6)) #define NMT_PAGE_IP6_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_IP6, NmtPageIP6Class)) @@ -44,6 +42,4 @@ GType nmt_page_ip6_get_type (void); NmtEditorPage *nmt_page_ip6_new (NMConnection *conn); -G_END_DECLS - #endif /* NMT_PAGE_IP6_H */ diff --git a/clients/tui/nmt-page-ppp.h b/clients/tui/nmt-page-ppp.h index 79103232..634323d4 100644 --- a/clients/tui/nmt-page-ppp.h +++ b/clients/tui/nmt-page-ppp.h @@ -21,8 +21,6 @@ #include "nmt-editor-page.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_PPP (nmt_page_ppp_get_type ()) #define NMT_PAGE_PPP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_PPP, NmtPagePpp)) #define NMT_PAGE_PPP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_PPP, NmtPagePppClass)) @@ -44,6 +42,4 @@ GType nmt_page_ppp_get_type (void); NmtEditorPage *nmt_page_ppp_new (NMConnection *conn); -G_END_DECLS - #endif /* NMT_PAGE_PPP_H */ diff --git a/clients/tui/nmt-page-team-port.h b/clients/tui/nmt-page-team-port.h index 2d8fa121..75c42849 100644 --- a/clients/tui/nmt-page-team-port.h +++ b/clients/tui/nmt-page-team-port.h @@ -21,8 +21,6 @@ #include "nmt-editor-page-device.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_TEAM_PORT (nmt_page_team_port_get_type ()) #define NMT_PAGE_TEAM_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPort)) #define NMT_PAGE_TEAM_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortClass)) @@ -44,6 +42,4 @@ GType nmt_page_team_port_get_type (void); NmtEditorPage *nmt_page_team_port_new (NMConnection *conn); -G_END_DECLS - #endif /* NMT_PAGE_TEAM_PORT_H */ diff --git a/clients/tui/nmt-page-team.h b/clients/tui/nmt-page-team.h index 994b63d3..a04c2a95 100644 --- a/clients/tui/nmt-page-team.h +++ b/clients/tui/nmt-page-team.h @@ -19,11 +19,8 @@ #ifndef NMT_PAGE_TEAM_H #define NMT_PAGE_TEAM_H -#include "nm-default.h" #include "nmt-editor-page-device.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_TEAM (nmt_page_team_get_type ()) #define NMT_PAGE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_TEAM, NmtPageTeam)) #define NMT_PAGE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_TEAM, NmtPageTeamClass)) @@ -46,6 +43,4 @@ GType nmt_page_team_get_type (void); NmtEditorPage *nmt_page_team_new (NMConnection *conn, NmtDeviceEntry *deventry); -G_END_DECLS - #endif /* NMT_PAGE_TEAM_H */ diff --git a/clients/tui/nmt-page-vlan.c b/clients/tui/nmt-page-vlan.c index c63b1904..9cd65421 100644 --- a/clients/tui/nmt-page-vlan.c +++ b/clients/tui/nmt-page-vlan.c @@ -115,7 +115,7 @@ nmt_page_vlan_constructed (GObject *object) nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); - widget = nmt_mac_entry_new (40, ETH_ALEN); + widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); diff --git a/clients/tui/nmt-page-vlan.h b/clients/tui/nmt-page-vlan.h index db7c4098..043c3bc0 100644 --- a/clients/tui/nmt-page-vlan.h +++ b/clients/tui/nmt-page-vlan.h @@ -21,8 +21,6 @@ #include "nmt-editor-page-device.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_VLAN (nmt_page_vlan_get_type ()) #define NMT_PAGE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_VLAN, NmtPageVlan)) #define NMT_PAGE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_VLAN, NmtPageVlanClass)) @@ -45,6 +43,4 @@ GType nmt_page_vlan_get_type (void); NmtEditorPage *nmt_page_vlan_new (NMConnection *conn, NmtDeviceEntry *deventry); -G_END_DECLS - #endif /* NMT_PAGE_VLAN_H */ diff --git a/clients/tui/nmt-page-wifi.c b/clients/tui/nmt-page-wifi.c index cd9b60e1..35625fe4 100644 --- a/clients/tui/nmt-page-wifi.c +++ b/clients/tui/nmt-page-wifi.c @@ -351,13 +351,13 @@ nmt_page_wifi_constructed (GObject *object) nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); - widget = nmt_mac_entry_new (40, ETH_ALEN); + widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_MAC); g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_BSSID, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); nmt_editor_grid_append (grid, _("BSSID"), widget, NULL); - widget = nmt_mac_entry_new (40, ETH_ALEN); + widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); diff --git a/clients/tui/nmt-page-wifi.h b/clients/tui/nmt-page-wifi.h index 7ce50584..ed16bdc5 100644 --- a/clients/tui/nmt-page-wifi.h +++ b/clients/tui/nmt-page-wifi.h @@ -21,8 +21,6 @@ #include "nmt-editor-page-device.h" -G_BEGIN_DECLS - #define NMT_TYPE_PAGE_WIFI (nmt_page_wifi_get_type ()) #define NMT_PAGE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_WIFI, NmtPageWifi)) #define NMT_PAGE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_WIFI, NmtPageWifiClass)) @@ -45,6 +43,4 @@ GType nmt_page_wifi_get_type (void); NmtEditorPage *nmt_page_wifi_new (NMConnection *conn, NmtDeviceEntry *deventry); -G_END_DECLS - #endif /* NMT_PAGE_WIFI_H */ diff --git a/clients/tui/nmt-password-dialog.h b/clients/tui/nmt-password-dialog.h index 36c9f51e..efdab7ec 100644 --- a/clients/tui/nmt-password-dialog.h +++ b/clients/tui/nmt-password-dialog.h @@ -21,8 +21,6 @@ #include "nmt-newt.h" -G_BEGIN_DECLS - #define NMT_TYPE_PASSWORD_DIALOG (nmt_password_dialog_get_type ()) #define NMT_PASSWORD_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialog)) #define NMT_PASSWORD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogClass)) @@ -52,6 +50,4 @@ gboolean nmt_password_dialog_succeeded (NmtPasswordDialog *dialog); const char *nmt_password_dialog_get_request_id (NmtPasswordDialog *dialog); GPtrArray *nmt_password_dialog_get_secrets (NmtPasswordDialog *dialog); -G_END_DECLS - #endif /* NMT_PASSWORD_DIALOG_H */ diff --git a/clients/tui/nmt-password-fields.h b/clients/tui/nmt-password-fields.h index 2ef830aa..6a6017ba 100644 --- a/clients/tui/nmt-password-fields.h +++ b/clients/tui/nmt-password-fields.h @@ -21,8 +21,6 @@ #include "nmt-newt.h" -G_BEGIN_DECLS - #define NMT_TYPE_PASSWORD_FIELDS (nmt_password_fields_get_type ()) #define NMT_PASSWORD_FIELDS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFields)) #define NMT_PASSWORD_FIELDS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsClass)) @@ -50,6 +48,4 @@ typedef enum { NmtNewtWidget *nmt_password_fields_new (int width, NmtPasswordFieldsExtras extras); -G_END_DECLS - #endif /* NMT_PASSWORD_FIELDS_H */ diff --git a/clients/tui/nmt-route-editor.h b/clients/tui/nmt-route-editor.h index 9882c713..febcb402 100644 --- a/clients/tui/nmt-route-editor.h +++ b/clients/tui/nmt-route-editor.h @@ -23,8 +23,6 @@ #include "nmt-newt.h" -G_BEGIN_DECLS - #define NMT_TYPE_ROUTE_EDITOR (nmt_route_editor_get_type ()) #define NMT_ROUTE_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditor)) #define NMT_ROUTE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorClass)) @@ -46,6 +44,4 @@ GType nmt_route_editor_get_type (void); NmtNewtForm *nmt_route_editor_new (NMSetting *setting); -G_END_DECLS - #endif /* NMT_ROUTE_EDITOR_H */ diff --git a/clients/tui/nmt-route-entry.h b/clients/tui/nmt-route-entry.h index d37b14cd..fa76fa7b 100644 --- a/clients/tui/nmt-route-entry.h +++ b/clients/tui/nmt-route-entry.h @@ -21,8 +21,6 @@ #include "nmt-newt.h" -G_BEGIN_DECLS - #define NMT_TYPE_ROUTE_ENTRY (nmt_route_entry_get_type ()) #define NMT_ROUTE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntry)) #define NMT_ROUTE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryClass)) @@ -46,6 +44,4 @@ NmtNewtWidget *nmt_route_entry_new (int family, int ip_entry_width, int metric_entry_width); -G_END_DECLS - #endif /* NMT_ROUTE_ENTRY_H */ diff --git a/clients/tui/nmt-route-table.h b/clients/tui/nmt-route-table.h index 217bf40d..1aa92e6b 100644 --- a/clients/tui/nmt-route-table.h +++ b/clients/tui/nmt-route-table.h @@ -21,8 +21,6 @@ #include "nmt-newt.h" -G_BEGIN_DECLS - #define NMT_TYPE_ROUTE_TABLE (nmt_route_table_get_type ()) #define NMT_ROUTE_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_TABLE, NmtRouteTable)) #define NMT_ROUTE_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_TABLE, NmtRouteTableClass)) @@ -44,6 +42,4 @@ GType nmt_route_table_get_type (void); NmtNewtWidget *nmt_route_table_new (int family); -G_END_DECLS - #endif /* NMT_ROUTE_TABLE_H */ diff --git a/clients/tui/nmt-slave-list.h b/clients/tui/nmt-slave-list.h index d10cd3a7..e0d836c7 100644 --- a/clients/tui/nmt-slave-list.h +++ b/clients/tui/nmt-slave-list.h @@ -22,8 +22,6 @@ #include "nmt-edit-connection-list.h" #include "nmtui-edit.h" -G_BEGIN_DECLS - #define NMT_TYPE_SLAVE_LIST (nmt_slave_list_get_type ()) #define NMT_SLAVE_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_SLAVE_LIST, NmtSlaveList)) #define NMT_SLAVE_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_SLAVE_LIST, NmtSlaveListClass)) @@ -47,6 +45,4 @@ NmtNewtWidget *nmt_slave_list_new (NMConnection *master, NmtAddConnectionTypeFilter type_filter, gpointer type_filter_data); -G_END_DECLS - #endif /* NMT_SLAVE_LIST_H */ diff --git a/clients/tui/nmt-utils.h b/clients/tui/nmt-utils.h index 5531dbeb..067c6512 100644 --- a/clients/tui/nmt-utils.h +++ b/clients/tui/nmt-utils.h @@ -19,10 +19,6 @@ #ifndef NMT_UTILS_H #define NMT_UTILS_H -#include "nm-default.h" - -G_BEGIN_DECLS - typedef struct { gpointer private[3]; } NmtSyncOp; @@ -41,6 +37,4 @@ void nmt_sync_op_complete_pointer (NmtSyncOp *op, gpointer result, GError *error); -G_END_DECLS - #endif /* NMT_UTILS_H */ diff --git a/clients/tui/nmt-widget-list.h b/clients/tui/nmt-widget-list.h index 7dbeb215..b7bf0151 100644 --- a/clients/tui/nmt-widget-list.h +++ b/clients/tui/nmt-widget-list.h @@ -21,8 +21,6 @@ #include "nmt-newt-grid.h" -G_BEGIN_DECLS - #define NMT_TYPE_WIDGET_LIST (nmt_widget_list_get_type ()) #define NMT_WIDGET_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_WIDGET_LIST, NmtWidgetList)) #define NMT_WIDGET_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_WIDGET_LIST, NmtWidgetListClass)) @@ -64,6 +62,4 @@ int nmt_widget_list_get_length (NmtWidgetList *list); void nmt_widget_list_set_length (NmtWidgetList *list, int length); -G_END_DECLS - #endif /* NMT_WIDGET_LIST_H */ diff --git a/clients/tui/nmtui-connect.c b/clients/tui/nmtui-connect.c index 46e33617..ae9dd43e 100644 --- a/clients/tui/nmtui-connect.c +++ b/clients/tui/nmtui-connect.c @@ -378,7 +378,7 @@ listbox_active_changed (GObject *object, } static NmtNewtForm * -nmt_connect_connection_list (void) +nmt_connect_connection_list (gboolean is_top) { int screen_width, screen_height; NmtNewtForm *form; @@ -410,7 +410,7 @@ nmt_connect_connection_list (void) listbox_active_changed (G_OBJECT (list), NULL, activate); g_signal_connect (activate, "clicked", G_CALLBACK (activate_clicked), list); - quit = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), _("Quit")); + quit = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), is_top ? _("Quit") : _("Back")); nmt_newt_widget_set_exit_on_activate (quit, TRUE); nmt_newt_form_set_content (form, grid); @@ -444,10 +444,10 @@ nmt_connect_connection (const char *identifier) } NmtNewtForm * -nmtui_connect (int argc, char **argv) +nmtui_connect (gboolean is_top, int argc, char **argv) { if (argc == 2) return nmt_connect_connection (argv[1]); else - return nmt_connect_connection_list (); + return nmt_connect_connection_list (is_top); } diff --git a/clients/tui/nmtui-connect.h b/clients/tui/nmtui-connect.h index 8310ac38..569d191a 100644 --- a/clients/tui/nmtui-connect.h +++ b/clients/tui/nmtui-connect.h @@ -19,10 +19,6 @@ #ifndef NMTUI_CONNECT_H #define NMTUI_CONNECT_H -G_BEGIN_DECLS - -NmtNewtForm *nmtui_connect (int argc, char **argv); - -G_END_DECLS +NmtNewtForm *nmtui_connect (gboolean is_top, int argc, char **argv); #endif /* NMTUI_CONNECT_H */ diff --git a/clients/tui/nmtui-edit.c b/clients/tui/nmtui-edit.c index 96c17245..a92e45a3 100644 --- a/clients/tui/nmtui-edit.c +++ b/clients/tui/nmtui-edit.c @@ -103,7 +103,7 @@ edit_connection_list_filter (NmtEditConnectionList *list, } static NmtNewtForm * -nmt_edit_main_connection_list (void) +nmt_edit_main_connection_list (gboolean is_top) { int screen_width, screen_height; NmtNewtForm *form; @@ -117,7 +117,7 @@ nmt_edit_main_connection_list (void) "escape-exits", TRUE, NULL); - quit = nmt_newt_button_new (_("Quit")); + quit = nmt_newt_button_new (is_top ? _("Quit") : _("Back")); nmt_newt_widget_set_exit_on_activate (quit, TRUE); list = g_object_new (NMT_TYPE_EDIT_CONNECTION_LIST, @@ -560,7 +560,7 @@ nmt_remove_connection (NMRemoteConnection *connection) } NmtNewtForm * -nmtui_edit (int argc, char **argv) +nmtui_edit (gboolean is_top, int argc, char **argv) { NMConnection *conn = NULL; @@ -577,5 +577,5 @@ nmtui_edit (int argc, char **argv) return nmt_editor_new (conn); } else - return nmt_edit_main_connection_list (); + return nmt_edit_main_connection_list (is_top); } diff --git a/clients/tui/nmtui-edit.h b/clients/tui/nmtui-edit.h index dae91cfa..041e7859 100644 --- a/clients/tui/nmtui-edit.h +++ b/clients/tui/nmtui-edit.h @@ -21,12 +21,10 @@ #include "nmt-newt.h" -G_BEGIN_DECLS - typedef gboolean (*NmtAddConnectionTypeFilter) (GType connection_type, gpointer user_data); -NmtNewtForm *nmtui_edit (int argc, char **argv); +NmtNewtForm *nmtui_edit (gboolean is_top, int argc, char **argv); void nmt_add_connection (void); void nmt_add_connection_full (const char *primary_text, @@ -39,6 +37,4 @@ void nmt_edit_connection (NMConnection *connection); void nmt_remove_connection (NMRemoteConnection *connection); -G_END_DECLS - #endif /* NMTUI_EDIT_H */ diff --git a/clients/tui/nmtui-hostname.c b/clients/tui/nmtui-hostname.c index ada2393f..f8005502 100644 --- a/clients/tui/nmtui-hostname.c +++ b/clients/tui/nmtui-hostname.c @@ -96,7 +96,7 @@ hostname_set (GObject *object, } NmtNewtForm * -nmtui_hostname (int argc, char **argv) +nmtui_hostname (gboolean is_top, int argc, char **argv) { const char *hostname; char *tmp = NULL; diff --git a/clients/tui/nmtui-hostname.h b/clients/tui/nmtui-hostname.h index a14bc699..772d9a72 100644 --- a/clients/tui/nmtui-hostname.h +++ b/clients/tui/nmtui-hostname.h @@ -19,10 +19,6 @@ #ifndef NMTUI_HOSTNAME_H #define NMTUI_HOSTNAME_H -G_BEGIN_DECLS - -NmtNewtForm *nmtui_hostname (int argc, char **argv); - -G_END_DECLS +NmtNewtForm *nmtui_hostname (gboolean is_top, int argc, char **argv); #endif /* NMTUI_HOSTNAME_H */ diff --git a/clients/tui/nmtui.c b/clients/tui/nmtui.c index 47ba5bf5..70cad5d6 100644 --- a/clients/tui/nmtui.c +++ b/clients/tui/nmtui.c @@ -43,7 +43,7 @@ NMClient *nm_client; static GMainLoop *loop; -typedef NmtNewtForm * (*NmtuiSubprogram) (int argc, char **argv); +typedef NmtNewtForm * (*NmtuiSubprogram) (gboolean is_top, int argc, char **argv); static const struct { const char *name, *shortcut, *arg; @@ -61,22 +61,43 @@ static const struct { nmtui_hostname } }; static const int num_subprograms = G_N_ELEMENTS (subprograms); +static NmtNewtForm *toplevel_form; -static void +static NmtNewtForm * quit_func (int argc, char **argv) { + if (toplevel_form) + nmt_newt_form_quit (toplevel_form); + nmtui_quit (); + + return NULL; +} + +static void +main_list_activated (NmtNewtWidget *widget, NmtNewtListbox *listbox) +{ + NmtNewtForm *form; + NmtuiSubprogram sub; + + sub = nmt_newt_listbox_get_active_key (listbox); + if (sub) { + form = sub (FALSE, 0, NULL); + if (form) { + nmt_newt_form_show (form); + g_object_unref (form); + } + } } static NmtNewtForm * -nmtui_main (int argc, char **argv) +nmtui_main (gboolean is_top, int argc, char **argv) { NmtNewtForm *form; NmtNewtWidget *widget, *ok; NmtNewtGrid *grid; NmtNewtListbox *listbox; NmtNewtButtonBox *bbox; - NmtuiSubprogram subprogram = NULL; int i; form = g_object_new (NMT_TYPE_NEWT_FORM, @@ -97,8 +118,9 @@ nmtui_main (int argc, char **argv) NULL); nmt_newt_grid_add (grid, widget, 0, 1); nmt_newt_widget_set_padding (widget, 0, 1, 0, 1); - nmt_newt_widget_set_exit_on_activate (widget, TRUE); listbox = NMT_NEWT_LISTBOX (widget); + g_signal_connect (widget, "activated", + G_CALLBACK (main_list_activated), listbox); for (i = 0; i < num_subprograms; i++) { nmt_newt_listbox_append (listbox, _(subprograms[i].display_name), @@ -112,17 +134,12 @@ nmtui_main (int argc, char **argv) bbox = NMT_NEWT_BUTTON_BOX (widget); ok = nmt_newt_button_box_add_end (bbox, _("OK")); - nmt_newt_widget_set_exit_on_activate (ok, TRUE); + g_signal_connect (ok, "activated", + G_CALLBACK (main_list_activated), listbox); - widget = nmt_newt_form_run_sync (form); - if (widget) - subprogram = nmt_newt_listbox_get_active_key (listbox); - g_object_unref (form); + toplevel_form = form; - if (subprogram) - return subprogram (argc, argv); - else - return NULL; + return form; } /** @@ -179,7 +196,7 @@ idle_run_subprogram (gpointer user_data) NmtuiStartupData *data = user_data; NmtNewtForm *form; - form = data->subprogram (data->argc, data->argv); + form = data->subprogram (TRUE, data->argc, data->argv); if (form) { g_signal_connect (form, "quit", G_CALLBACK (toplevel_form_quit), NULL); nmt_newt_form_show (form); diff --git a/clients/tui/nmtui.h b/clients/tui/nmtui.h index aada9719..53c20cb4 100644 --- a/clients/tui/nmtui.h +++ b/clients/tui/nmtui.h @@ -21,12 +21,8 @@ #include <NetworkManager.h> -G_BEGIN_DECLS - extern NMClient *nm_client; void nmtui_quit (void); -G_END_DECLS - #endif /* NMTUI_H */ |