Commit 9a0e4bae authored by Jiří Klimeš's avatar Jiří Klimeš

cli: add 'nmcli connection import' (rh #1034105)

Synopsis:
  nmcli connection import [--temporary] type <type> file <file to import>

Only VPN configurations can be imported at the moment.

https://bugzilla.redhat.com/show_bug.cgi?id=1034105
parent f28d311d
......@@ -40,6 +40,8 @@ nmcli_SOURCES = \
\
$(srcdir)/../common/nm-secret-agent-simple.c \
$(srcdir)/../common/nm-secret-agent-simple.h \
$(srcdir)/../common/nm-vpn-helpers.c \
$(srcdir)/../common/nm-vpn-helpers.h \
$(NULL)
nmcli_LDADD = \
......
......@@ -36,6 +36,7 @@
#include "connections.h"
#include "nm-secret-agent-simple.h"
#include "polkit-agent.h"
#include "nm-vpn-helpers.h"
/* define some prompts for connection editor */
#define EDITOR_PROMPT_SETTING _("Setting name? ")
......@@ -274,7 +275,8 @@ usage (void)
" delete [id | uuid | path] <ID>\n\n"
" monitor [id | uuid | path] <ID> ...\n\n"
" reload\n\n"
" load <filename> [ <filename>... ]\n\n"));
" load <filename> [ <filename>... ]\n\n"
" import [--temporary] type <type> file <file to import>\n\n"));
}
static void
......@@ -522,6 +524,19 @@ usage_connection_load (void)
"state.\n\n"));
}
static void
usage_connection_import (void)
{
g_printerr (_("Usage: nmcli connection import { ARGUMENTS | help }\n"
"\n"
"ARGUMENTS := [--temporary] type <type> file <file to import>\n"
"\n"
"Import an external/foreign configuration as a NetworkManager connection profile.\n"
"The type of the input file is specified by type option.\n"
"Only VPN configurations are supported at the moment. The configuration\n"
"is imported by NetworkManager VPN plugins.\n\n"));
}
static gboolean
usage_connection_second_level (const char *cmd)
{
......@@ -549,6 +564,8 @@ usage_connection_second_level (const char *cmd)
usage_connection_reload ();
else if (matches (cmd, "load") == 0)
usage_connection_load ();
else if (matches (cmd, "import") == 0)
usage_connection_import ();
else
ret = FALSE;
return ret;
......@@ -9967,6 +9984,114 @@ do_connection_load (NmCli *nmc, int argc, char **argv)
return nmc->return_value;
}
// 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: ")
static NMCResultCode
do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv)
{
GError *error = NULL;
const char *type = NULL, *filename = NULL;
char *type_ask = NULL, *filename_ask = NULL;
AddConnectionInfo *info;
NMConnection *connection = NULL;
NMVpnEditorPlugin *plugin;
if (argc == 0) {
if (nmc->ask) {
type_ask = nmc_readline (PROMPT_IMPORT_TYPE);
filename_ask = nmc_readline (PROMPT_IMPORT_FILE);
type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
filename = filename_ask = filename_ask ? g_strstrip (filename_ask) : NULL;
} else {
g_string_printf (nmc->return_text, _("Error: No arguments provided."));
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
goto finish;
}
}
while (argc > 0) {
if (strcmp (*argv, "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 finish;
}
if (!type)
type = *argv;
else
g_printerr (_("Warning: 'type' already specified, ignoring extra one.\n"));
} else if (strcmp (*argv, "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 finish;
}
if (!filename)
filename = *argv;
else
g_printerr (_("Warning: 'file' already specified, ignoring extra one.\n"));
} else {
g_string_printf (nmc->return_text, _("Unknown parameter: %s\n"), *argv);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
goto finish;
}
argc--;
argv++;
}
if (!type) {
g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
goto finish;
}
if (!filename) {
g_string_printf (nmc->return_text, _("Error: 'file' argument is required."));
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
goto finish;
}
/* Import VPN configuration */
plugin = nm_vpn_get_plugin_by_service (type, &error);
if (!plugin) {
g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
error->message);
nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
goto finish;
}
connection = nm_vpn_editor_plugin_import (plugin, filename, &error);
if (!connection) {
g_string_printf (nmc->return_text, _("Error: failed to import '%s': %s."),
filename, error->message);
nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
goto finish;
}
info = g_malloc0 (sizeof (AddConnectionInfo));
info->nmc = nmc;
info->con_name = g_strdup (nm_connection_get_id (connection));
/* Add the new imported connection to NetworkManager */
add_new_connection (!temporary,
nmc->client,
connection,
add_connection_cb,
info);
nmc->should_wait = TRUE;
finish:
if (connection)
g_object_unref (connection);
g_clear_error (&error);
g_free (type_ask);
g_free (filename_ask);
return nmc->return_value;
}
typedef struct {
NmCli *nmc;
......@@ -10067,6 +10192,11 @@ nmcli_con_tab_completion (const char *text, int start, int end)
generator_func = gen_func_connection_names;
} else if (g_strcmp0 (rl_prompt, PROMPT_ACTIVE_CONNECTIONS) == 0) {
generator_func = gen_func_active_connection_names;
} else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_TYPE) == 0) {
generator_func = gen_func_vpn_types;
} else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_FILE) == 0) {
rl_attempted_completion_over = 0;
rl_complete_with_tilde_expansion = 1;
}
if (generator_func)
......@@ -10250,6 +10380,15 @@ do_connections (NmCli *nmc, int argc, char **argv)
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 {
usage ();
g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv);
......
......@@ -282,6 +282,7 @@ _nmcli_compl_OPTIONS()
# expects several options with parameters. This function can parse them and remove them from the words array.
_nmcli_compl_ARGS()
{
local aliases=${@}
local OPTIONS_ALL N_REMOVE_WORDS REMOVE_OPTIONS OPTIONS_HAS_MANDATORY i
OPTIONS_ALL=("${OPTIONS[@]}")
OPTIONS_UNKNOWN_OPTION=
......@@ -317,7 +318,17 @@ _nmcli_compl_ARGS()
N_REMOVE_WORDS=2
REMOVE_OPTIONS=("${words[0]}")
case "${words[0]}" in
# change option name to alias
WORD0="${words[0]}"
for alias in "${aliases[@]}" ; do
if [[ "${WORD0}" == ${alias%%:*} ]]; then
WORD0=${alias#*:}
break
fi
done
case "${WORD0}" in
level)
if [[ "${#words[@]}" -eq 2 ]]; then
_nmcli_list "OFF ERR WARN INFO DEBUG TRACE"
......@@ -560,7 +571,8 @@ _nmcli_compl_ARGS()
username| \
service| \
password| \
passwd-file)
passwd-file| \
file)
if [[ "${#words[@]}" -eq 2 ]]; then
return 0
fi
......@@ -870,7 +882,7 @@ _nmcli()
;;
c|co|con|conn|conne|connec|connect|connecti|connectio|connection)
if [[ ${#words[@]} -eq 2 ]]; then
_nmcli_compl_COMMAND "$command" show up down add modify clone edit delete monitor reload load
_nmcli_compl_COMMAND "$command" show up down add modify clone edit delete monitor reload load import
elif [[ ${#words[@]} -gt 2 ]]; then
case "$command" in
s|sh|sho|show)
......@@ -1315,6 +1327,35 @@ _nmcli()
COMPREPLY=()
fi
;;
i|im|imp|impo|impor|import)
if [[ ${#words[@]} -eq 3 ]]; then
_nmcli_compl_COMMAND "${words[2]}" type file --temporary
elif [[ ${#words[@]} -gt 3 ]]; then
_nmcli_array_delete_at words 0 1
LONG_OPTIONS=(help temporary)
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 file
fi
return 0
;;
esac
OPTIONS=(type file)
OPTIONS_MANDATORY=(type file)
ALIASES=("type:vpn-type")
_nmcli_compl_ARGS ${ALIASES[@]}
return 0
fi
;;
esac
fi
;;
......
......@@ -816,6 +816,21 @@ then \fINetworkManager\fP will reload connection files any time they change
Load/reload one or more connection files from disk. Use this after manually
editing a connection file to ensure that \fBNetworkManager\fP is aware
of its latest state.
.TP
.B import [--temporary] type <type> file <file to import>
.br
Import an external/foreign configuration as a NetworkManager connection profile.
The type of the input file is specified by \fItype\fP option.
.br
Only VPN configurations are supported at the moment. The configuration
is imported by NetworkManager VPN plugins. \fItype\fP values are the same as for
\fIvpn-type\fP option in \fBnmcli connection add\fP. VPN configurations are
imported by VPN plugins. Therefore the proper VPN plugin has to be installed
so that nmcli could import the data.
.br
The imported connection profile will be saved as persistent unless \fI--temporary\fP
option is specified, in which case the new profile won't exist after NetworkManager
restart.
.RE
.TP
......@@ -1187,6 +1202,10 @@ appends a Google public DNS server to DNS servers in ABC profile.
.IP
removes the specified IP address from (static) profile ABC.
.IP "\fB\f(CWnmcli con import type openvpn file ~/Downloads/frootvpn.ovpn\fP\fP"
.IP
imports an OpenVPN configuration to NetworkManager.
.SH NOTES
\fInmcli\fP accepts abbreviations, as long as they are a unique prefix in the set
of possible options. As new options get added, these abbreviations are not guaranteed
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment