cli: add 'nmcli connection export' (rh #1034105)
Synopsis: nmcli connection export [ id | uuid | path] <ID> [<output file>] for exporting VPN connections. https://bugzilla.redhat.com/show_bug.cgi?id=1034105
This commit is contained in:
@@ -48,6 +48,7 @@
|
|||||||
#define PROMPT_VPN_TYPE _("VPN type: ")
|
#define PROMPT_VPN_TYPE _("VPN type: ")
|
||||||
#define PROMPT_MASTER _("Master: ")
|
#define PROMPT_MASTER _("Master: ")
|
||||||
#define PROMPT_CONNECTION _("Connection (name, UUID, or path): ")
|
#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_CONNECTIONS _("Connection(s) (name, UUID, or path): ")
|
||||||
#define PROMPT_ACTIVE_CONNECTIONS _("Connection(s) (name, UUID, path or apath): ")
|
#define PROMPT_ACTIVE_CONNECTIONS _("Connection(s) (name, UUID, path or apath): ")
|
||||||
|
|
||||||
@@ -276,7 +277,8 @@ usage (void)
|
|||||||
" monitor [id | uuid | path] <ID> ...\n\n"
|
" monitor [id | uuid | path] <ID> ...\n\n"
|
||||||
" reload\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"));
|
" import [--temporary] type <type> file <file to import>\n\n"
|
||||||
|
" export [id | uuid | path] <ID> [<output file>]\n\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -537,6 +539,17 @@ usage_connection_import (void)
|
|||||||
"is imported by NetworkManager VPN plugins.\n\n"));
|
"is imported by NetworkManager VPN plugins.\n\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage_connection_export (void)
|
||||||
|
{
|
||||||
|
g_printerr (_("Usage: nmcli connection export { ARGUMENTS | help }\n"
|
||||||
|
"\n"
|
||||||
|
"ARGUMENTS := [id | uuid | path] <ID> [<output file>]\n"
|
||||||
|
"\n"
|
||||||
|
"Export a connection. Only VPN connections are supported at the moment.\n"
|
||||||
|
"The data are directed to standard output or to a file if a name is given.\n\n"));
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
usage_connection_second_level (const char *cmd)
|
usage_connection_second_level (const char *cmd)
|
||||||
{
|
{
|
||||||
@@ -566,6 +579,8 @@ usage_connection_second_level (const char *cmd)
|
|||||||
usage_connection_load ();
|
usage_connection_load ();
|
||||||
else if (matches (cmd, "import") == 0)
|
else if (matches (cmd, "import") == 0)
|
||||||
usage_connection_import ();
|
usage_connection_import ();
|
||||||
|
else if (matches (cmd, "export") == 0)
|
||||||
|
usage_connection_export ();
|
||||||
else
|
else
|
||||||
ret = FALSE;
|
ret = FALSE;
|
||||||
return ret;
|
return ret;
|
||||||
@@ -6885,33 +6900,59 @@ gen_compat_devices (const char *text, int state)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char **
|
||||||
|
_create_vpn_array (const GPtrArray *connections, gboolean uuid)
|
||||||
|
{
|
||||||
|
int c, idx = 0;
|
||||||
|
const char **array;
|
||||||
|
|
||||||
|
if (connections->len < 1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
array = g_new (const char *, connections->len + 1);
|
||||||
|
for (c = 0; c < connections->len; c++) {
|
||||||
|
NMConnection *connection = NM_CONNECTION (connections->pdata[c]);
|
||||||
|
const char *type = nm_connection_get_connection_type (connection);
|
||||||
|
|
||||||
|
if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) == 0)
|
||||||
|
array[idx++] = uuid ? nm_connection_get_uuid (connection) : nm_connection_get_id (connection);
|
||||||
|
}
|
||||||
|
array[idx] = NULL;
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
gen_vpn_uuids (const char *text, int state)
|
gen_vpn_uuids (const char *text, int state)
|
||||||
{
|
{
|
||||||
const GPtrArray *connections = nmc_tab_completion.nmc->connections;
|
const GPtrArray *connections = nm_cli.connections;
|
||||||
int c, u = 0;
|
|
||||||
const char **uuids;
|
const char **uuids;
|
||||||
char *ret;
|
char *ret;
|
||||||
|
|
||||||
if (connections->len < 1)
|
if (connections->len < 1)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
uuids = g_new (const char *, connections->len + 1);
|
uuids = _create_vpn_array (connections, TRUE);
|
||||||
for (c = 0; c < connections->len; c++) {
|
|
||||||
NMConnection *connection = NM_CONNECTION (connections->pdata[c]);
|
|
||||||
const char *type = nm_connection_get_connection_type (connection);
|
|
||||||
|
|
||||||
if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) == 0)
|
|
||||||
uuids[u++] = nm_connection_get_uuid (connection);
|
|
||||||
}
|
|
||||||
uuids[u] = NULL;
|
|
||||||
|
|
||||||
ret = nmc_rl_gen_func_basic (text, state, uuids);
|
ret = nmc_rl_gen_func_basic (text, state, uuids);
|
||||||
|
|
||||||
g_free (uuids);
|
g_free (uuids);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
gen_vpn_ids (const char *text, int state)
|
||||||
|
{
|
||||||
|
const GPtrArray *connections = nm_cli.connections;
|
||||||
|
const char **ids;
|
||||||
|
char *ret;
|
||||||
|
|
||||||
|
if (connections->len < 1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ids = _create_vpn_array (connections, FALSE);
|
||||||
|
ret = nmc_rl_gen_func_basic (text, state, ids);
|
||||||
|
g_free (ids);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static rl_compentry_func_t *
|
static rl_compentry_func_t *
|
||||||
get_gen_func_cmd_nmcli (const char *str)
|
get_gen_func_cmd_nmcli (const char *str)
|
||||||
{
|
{
|
||||||
@@ -10092,6 +10133,127 @@ finish:
|
|||||||
return nmc->return_value;
|
return nmc->return_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
g_string_printf (nmc->return_text, _("Error: connection ID 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;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = nm_connection_get_connection_type (connection);
|
||||||
|
if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) != 0) {
|
||||||
|
g_string_printf (nmc->return_text, _("Error: the connection is not VPN."));
|
||||||
|
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
|
||||||
|
|
||||||
|
/* Export 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."));
|
||||||
|
nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_name)
|
||||||
|
path = out_name;
|
||||||
|
else {
|
||||||
|
int fd;
|
||||||
|
char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX";
|
||||||
|
fd = g_mkstemp (tmpfile);
|
||||||
|
if (fd == -1) {
|
||||||
|
g_string_printf (nmc->return_text, _("Error: failed to create temporary file %s."), tmpfile);
|
||||||
|
nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
close (fd);
|
||||||
|
path = tmpfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nm_vpn_editor_plugin_export (plugin, path, connection, &error)) {
|
||||||
|
g_string_printf (nmc->return_text, _("Error: failed to export '%s': %s."),
|
||||||
|
nm_connection_get_id (connection), error ? error->message : "(unknown)");
|
||||||
|
nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No output file -> copy data to stdout */
|
||||||
|
if (!out_name) {
|
||||||
|
char *contents = NULL;
|
||||||
|
gsize len = 0;
|
||||||
|
if (!g_file_get_contents (path, &contents, &len, &error)) {
|
||||||
|
g_string_printf (nmc->return_text, _("Error: failed to read temporary file '%s': %s."),
|
||||||
|
path, error->message);
|
||||||
|
nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
g_print ("%s", contents);
|
||||||
|
g_free (contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
finish:
|
||||||
|
if (!out_name && path)
|
||||||
|
unlink (path);
|
||||||
|
g_clear_error (&error);
|
||||||
|
g_free (name_ask);
|
||||||
|
g_free (out_name_ask);
|
||||||
|
return nmc->return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
NmCli *nmc;
|
NmCli *nmc;
|
||||||
@@ -10197,6 +10359,8 @@ nmcli_con_tab_completion (const char *text, int start, int end)
|
|||||||
} else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_FILE) == 0) {
|
} else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_FILE) == 0) {
|
||||||
rl_attempted_completion_over = 0;
|
rl_attempted_completion_over = 0;
|
||||||
rl_complete_with_tilde_expansion = 1;
|
rl_complete_with_tilde_expansion = 1;
|
||||||
|
} else if (g_strcmp0 (rl_prompt, PROMPT_VPN_CONNECTION) == 0) {
|
||||||
|
generator_func = gen_vpn_ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (generator_func)
|
if (generator_func)
|
||||||
@@ -10389,6 +10553,8 @@ do_connections (NmCli *nmc, int argc, char **argv)
|
|||||||
next_arg (&argc, &argv);
|
next_arg (&argc, &argv);
|
||||||
}
|
}
|
||||||
nmc->return_value = do_connection_import (nmc, temporary, 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 {
|
} else {
|
||||||
usage ();
|
usage ();
|
||||||
g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv);
|
g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv);
|
||||||
|
@@ -882,7 +882,7 @@ _nmcli()
|
|||||||
;;
|
;;
|
||||||
c|co|con|conn|conne|connec|connect|connecti|connectio|connection)
|
c|co|con|conn|conne|connec|connect|connecti|connectio|connection)
|
||||||
if [[ ${#words[@]} -eq 2 ]]; then
|
if [[ ${#words[@]} -eq 2 ]]; then
|
||||||
_nmcli_compl_COMMAND "$command" show up down add modify clone edit delete monitor reload load import
|
_nmcli_compl_COMMAND "$command" show up down add modify clone edit delete monitor reload load import export
|
||||||
elif [[ ${#words[@]} -gt 2 ]]; then
|
elif [[ ${#words[@]} -gt 2 ]]; then
|
||||||
case "$command" in
|
case "$command" in
|
||||||
s|sh|sho|show)
|
s|sh|sho|show)
|
||||||
@@ -1355,6 +1355,32 @@ _nmcli()
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
e|ex|exp|expo|expor|export)
|
||||||
|
if [[ ${#words[@]} -eq 3 ]]; then
|
||||||
|
_nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")"
|
||||||
|
elif [[ ${#words[@]} -gt 3 ]]; then
|
||||||
|
_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_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
OPTIONS=(id uuid path)
|
||||||
|
_nmcli_compl_ARGS_CONNECTION && return 0
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
@@ -831,6 +831,14 @@ so that nmcli could import the data.
|
|||||||
The imported connection profile will be saved as persistent unless \fI--temporary\fP
|
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
|
option is specified, in which case the new profile won't exist after NetworkManager
|
||||||
restart.
|
restart.
|
||||||
|
.TP
|
||||||
|
.B export [ id | uuid | path ] <ID> [<output file>]
|
||||||
|
.br
|
||||||
|
Export a connection.
|
||||||
|
.br
|
||||||
|
Only VPN connections are supported at the moment. A proper VPN plugin has to be
|
||||||
|
installed so that nmcli could export a connection. If no \fI<output file>\fP is
|
||||||
|
provided, the VPN configuration data will be printed to standard output.
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
@@ -1206,6 +1214,10 @@ removes the specified IP address from (static) profile ABC.
|
|||||||
.IP
|
.IP
|
||||||
imports an OpenVPN configuration to NetworkManager.
|
imports an OpenVPN configuration to NetworkManager.
|
||||||
|
|
||||||
|
.IP "\fB\f(CWnmcli con export corp-vpnc /home/joe/corpvpn.conf\fP\fP"
|
||||||
|
.IP
|
||||||
|
exports NetworkManager VPN profile corp-vpnc as standard Cisco (vpnc) configuration.
|
||||||
|
|
||||||
.SH NOTES
|
.SH NOTES
|
||||||
\fInmcli\fP accepts abbreviations, as long as they are a unique prefix in the set
|
\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
|
of possible options. As new options get added, these abbreviations are not guaranteed
|
||||||
|
Reference in New Issue
Block a user