diff options
author | Thomas Haller <thaller@redhat.com> | 2020-04-04 15:10:05 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2020-04-04 19:49:09 +0200 |
commit | 4987a7cd6bf63c70c5d96ea90cbcfb6f7f148379 (patch) | |
tree | 9e3a7468da274a60fd12985745d6c30690a5f841 | |
parent | 306414b93d447e915b6de94b9b7410ff7aadc4cf (diff) | |
download | NetworkManager-th/cli-globals-2.tar.gz |
xxxxxxth/cli-globals-2
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | clients/cli/agent.c | 153 | ||||
-rw-r--r-- | clients/cli/agent.h | 13 | ||||
-rw-r--r-- | clients/cli/common.c | 209 | ||||
-rw-r--r-- | clients/cli/common.h | 25 | ||||
-rw-r--r-- | clients/cli/connections.c | 821 | ||||
-rw-r--r-- | clients/cli/connections.h | 2 | ||||
-rw-r--r-- | clients/cli/general.c | 777 | ||||
-rw-r--r-- | clients/cli/general.h | 17 | ||||
-rw-r--r-- | clients/cli/nmcli.c | 502 | ||||
-rw-r--r-- | clients/cli/nmcli.h | 195 | ||||
-rw-r--r-- | clients/cli/utils.c | 27 | ||||
-rw-r--r-- | clients/cli/utils.h | 30 |
13 files changed, 1596 insertions, 1181 deletions
diff --git a/Makefile.am b/Makefile.am index e1b7b12abd..8fce37b982 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4479,17 +4479,15 @@ clients_cli_nmcli_SOURCES = \ clients/cli/utils.c \ clients/cli/utils.h \ clients/cli/agent.c \ - clients/cli/agent.h \ clients/cli/general.c \ - clients/cli/general.h \ + clients/cli/nmcli.c \ + clients/cli/nmcli.h \ clients/cli/connections.c \ clients/cli/connections.h \ clients/cli/devices.c \ clients/cli/devices.h \ clients/cli/settings.c \ clients/cli/settings.h \ - clients/cli/nmcli.c \ - clients/cli/nmcli.h \ clients/cli/polkit-agent.c \ clients/cli/polkit-agent.h \ $(NULL) diff --git a/clients/cli/agent.c b/clients/cli/agent.c index 93b5ca079a..d82c251454 100644 --- a/clients/cli/agent.c +++ b/clients/cli/agent.c @@ -5,8 +5,6 @@ #include "nm-default.h" -#include "agent.h" - #include <stdio.h> #include <stdlib.h> #include <readline/readline.h> @@ -70,6 +68,7 @@ set_deftext (void) static gboolean get_secrets_from_user (const NmcConfig *nmc_config, + NmcReadlineStatus *readline_status, const char *request_id, const char *title, const char *msg, @@ -90,9 +89,9 @@ get_secrets_from_user (const NmcConfig *nmc_config, pre_input_deftext = g_strdup (secret->value); } if (secret->no_prompt_entry_id) - pwd = nmc_readline (nmc_config, "%s: ", secret->pretty_name); + pwd = nmc_readline (nmc_config, readline_status, "%s: ", secret->pretty_name); else - pwd = nmc_readline (nmc_config, "%s (%s): ", secret->pretty_name, secret->entry_id); + pwd = nmc_readline (nmc_config, readline_status, "%s (%s): ", secret->pretty_name, secret->entry_id); /* No password provided, cancel the secrets. */ if (!pwd) @@ -117,39 +116,12 @@ secrets_requested (NMSecretAgentSimple *agent, if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) nmc_terminal_erase_line (); - success = get_secrets_from_user (&nmc->nmc_config, request_id, title, msg, secrets); + success = get_secrets_from_user (&nmc->nmc_config, &nmc->readline_status, request_id, title, msg, secrets); nm_secret_agent_simple_response (agent, request_id, success ? secrets : NULL); } -static NMCResultCode -do_agent_secret (NmCli *nmc, int argc, char **argv) -{ - next_arg (nmc, &argc, &argv, NULL); - if (nmc->complete) - return nmc->return_value; - - /* Create secret agent */ - nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-agent"); - if (nmc->secret_agent) { - /* We keep running */ - nmc->should_wait++; - - nm_secret_agent_simple_enable (nmc->secret_agent, NULL); - g_signal_connect (nmc->secret_agent, - NM_SECRET_AGENT_SIMPLE_REQUEST_SECRETS, - G_CALLBACK (secrets_requested), - nmc); - g_print (_("nmcli successfully registered as a NetworkManager's secret agent.\n")); - } else { - g_string_printf (nmc->return_text, _("Error: secret agent initialization failed")); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - } - - return nmc->return_value; -} - static void polkit_registered (gpointer instance, gpointer user_data) @@ -162,83 +134,80 @@ polkit_error (gpointer instance, const char *error, gpointer user_data) { - g_main_loop_quit (loop); + NmCli *nmc = user_data; + + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_UNKNOWN, + _("Error: polkit agent failed: %s"), + error); } -static NMCResultCode -do_agent_polkit (NmCli *nmc, int argc, char **argv) +static void +do_agent (const NMCCommand *cmd, + NmCli *nmc, + int argc, + const char *const*argv) { gs_free_error GError *error = NULL; + gboolean with_polkit_agent = NM_IN_STRSET (cmd->cmd, NULL, "all", "polkit"); + gboolean with_secret_agent = NM_IN_STRSET (cmd->cmd, NULL, "all", "secret"); + + nm_assert (with_polkit_agent || with_secret_agent); + + if (with_polkit_agent) { + if (!nmc_polkit_agent_init (nmc, TRUE, &error)) { + g_dbus_error_strip_remote_error (error); + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_UNKNOWN, + _("Error: polkit agent initialization failed: %s"), + error->message); + return; + } + } - next_arg (nmc, &argc, &argv, NULL); - if (nmc->complete) - return nmc->return_value; - - if (!nmc_polkit_agent_init (nmc, TRUE, &error)) { - g_dbus_error_strip_remote_error (error); - g_string_printf (nmc->return_text, - _("Error: polkit agent initialization failed: %s"), - error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - } else { - /* We keep running */ - nmc->should_wait++; + if (with_secret_agent) { + nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-agent"); + if (!nmc->secret_agent) { + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_UNKNOWN, + _("Error: secret agent initialization failed")); + return; + } + } + + nmc_run_status_wait_push (&nmc->run_status); + + if (with_polkit_agent) { g_signal_connect (nmc->pk_listener, NM_POLKIT_LISTENER_SIGNAL_ERROR, G_CALLBACK (polkit_error), - NULL); + nmc); g_signal_connect (nmc->pk_listener, NM_POLKIT_LISTENER_SIGNAL_REGISTERED, G_CALLBACK (polkit_registered), - NULL); - - /* keep running */ - nmc->should_wait++; - } - - return nmc->return_value; -} - -static NMCResultCode -do_agent_all (NmCli *nmc, int argc, char **argv) -{ - NMCResultCode secret_res; - - next_arg (nmc, &argc, &argv, NULL); - 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) { - g_printerr ("%s\n", nmc->return_text->str); - g_string_truncate (nmc->return_text, 0); + nmc); } - nmc->return_value = do_agent_polkit (nmc, argc, argv); - if (nmc->return_value != NMC_RESULT_SUCCESS) { - g_printerr ("%s\n", nmc->return_text->str); - g_string_truncate (nmc->return_text, 0); + if (with_secret_agent) { + nm_secret_agent_simple_enable (nmc->secret_agent, NULL); + g_signal_connect (nmc->secret_agent, + NM_SECRET_AGENT_SIMPLE_REQUEST_SECRETS, + G_CALLBACK (secrets_requested), + nmc); + g_print (_("nmcli successfully registered as a NetworkManager's secret agent.\n")); } - - if (nmc->return_value == NMC_RESULT_SUCCESS && secret_res != NMC_RESULT_SUCCESS) - nmc->return_value = secret_res; - - return nmc->return_value; } -static const NMCCommand agent_cmds[] = { - { "secret", do_agent_secret, usage_agent_secret, TRUE, TRUE }, - { "polkit", do_agent_polkit, usage_agent_polkit, TRUE, TRUE }, - { "all", do_agent_all, usage_agent_all, TRUE, TRUE }, - { NULL, do_agent_all, usage, TRUE, TRUE }, -}; - -NMCResultCode -do_agent (NmCli *nmc, int argc, char **argv) +void +nmc_command_do_agent (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { + static const NMCCommand agent_cmds[] = { + { "secret", do_agent, usage_agent_secret, TRUE, TRUE }, + { "polkit", do_agent, usage_agent_polkit, TRUE, TRUE }, + { "all", do_agent, usage_agent_all, TRUE, TRUE }, + { NULL, do_agent, usage, TRUE, TRUE }, + }; + next_arg (nmc, &argc, &argv, NULL); nmc_do_cmd (nmc, agent_cmds, *argv, argc, argv); - - return nmc->return_value; } diff --git a/clients/cli/agent.h b/clients/cli/agent.h deleted file mode 100644 index aad56bba1c..0000000000 --- a/clients/cli/agent.h +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2014 Red Hat, Inc. - */ - -#ifndef __NMC_AGENT_H__ -#define __NMC_AGENT_H__ - -#include "nmcli.h" - -NMCResultCode do_agent (NmCli *nmc, int argc, char **argv); - -#endif /* __NMC_AGENT_H__ */ diff --git a/clients/cli/common.c b/clients/cli/common.c index 7e9bcba860..14285b71bf 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -672,6 +672,7 @@ vpn_openconnect_get_secrets (NMConnection *connection, GPtrArray *secrets) static gboolean get_secrets_from_user (const NmcConfig *nmc_config, + NmcReadlineStatus *readline_status, const char *request_id, const char *title, const char *msg, @@ -716,9 +717,9 @@ get_secrets_from_user (const NmcConfig *nmc_config, : TRUE; if (secret->no_prompt_entry_id) - pwd = nmc_readline_echo (nmc_config, echo_on, "%s: ", secret->pretty_name); + pwd = nmc_readline_echo (nmc_config, readline_status, echo_on, "%s: ", secret->pretty_name); else - pwd = nmc_readline_echo (nmc_config, echo_on, "%s (%s): ", secret->pretty_name, secret->entry_id); + pwd = nmc_readline_echo (nmc_config, readline_status, echo_on, "%s (%s): ", secret->pretty_name, secret->entry_id); if (!pwd) pwd = g_strdup (""); @@ -782,6 +783,7 @@ nmc_secrets_requested (NMSecretAgentSimple *agent, } success = get_secrets_from_user (&nmc->nmc_config, + &nmc->readline_status, request_id, title, msg, @@ -826,7 +828,6 @@ nmc_unique_connection_name (const GPtrArray *connections, const char *try_name) } /* readline state variables */ -static gboolean nmcli_in_readline = FALSE; static gboolean rl_got_line; static char *rl_string; @@ -844,15 +845,15 @@ nmc_cleanup_readline (void) } gboolean -nmc_get_in_readline (void) +nmc_get_in_readline (NmcReadlineStatus *readline_status) { - return nmcli_in_readline; + return readline_status->in_readline; } void -nmc_set_in_readline (gboolean in_readline) +nmc_set_in_readline (NmcReadlineStatus *readline_status, gboolean in_readline) { - nmcli_in_readline = in_readline; + readline_status->in_readline = in_readline; } static void @@ -874,11 +875,12 @@ stdin_ready_cb (int fd, static char * nmc_readline_helper (const NmcConfig *nmc_config, + NmcReadlineStatus *readline_status, const char *prompt) { GSource *io_source; - nmc_set_in_readline (TRUE); + nmc_set_in_readline (readline_status, TRUE); io_source = nm_g_unix_fd_source_new (STDIN_FILENO, G_IO_IN, @@ -895,11 +897,11 @@ read_again: while ( !rl_got_line && g_main_loop_is_running (loop) - && !nmc_seen_sigint ()) + && !nmc_seen_sigint (readline_status)) g_main_context_iteration (NULL, TRUE); /* If Ctrl-C was detected, complete the line */ - if (nmc_seen_sigint ()) { + if (nmc_seen_sigint (readline_status)) { rl_echo_signal_char (SIGINT); if (!rl_got_line) { rl_stuff_char ('\n'); @@ -911,9 +913,9 @@ read_again: if (rl_string && *rl_string) add_history (rl_string); - if (nmc_seen_sigint ()) { + if (nmc_seen_sigint (readline_status)) { /* Ctrl-C */ - nmc_clear_sigint (); + nmc_clear_sigint (readline_status); if ( nmc_config->in_editor || (rl_string && *rl_string)) { /* In editor, or the line is not empty */ @@ -937,7 +939,7 @@ read_again: nm_clear_g_source_inst (&io_source); - nmc_set_in_readline (FALSE); + nmc_set_in_readline (readline_status, FALSE); return rl_string; } @@ -958,18 +960,19 @@ read_again: */ char * nmc_readline (const NmcConfig *nmc_config, + NmcReadlineStatus *readline_status, const char *prompt_fmt, ...) { - va_list args; gs_free char *prompt = NULL; + va_list args; rl_initialize (); va_start (args, prompt_fmt); prompt = g_strdup_vprintf (prompt_fmt, args); va_end (args); - return nmc_readline_helper (nmc_config, prompt); + return nmc_readline_helper (nmc_config, readline_status, prompt); } static void @@ -1005,6 +1008,7 @@ nmc_secret_redisplay (void) */ char * nmc_readline_echo (const NmcConfig *nmc_config, + NmcReadlineStatus *readline_status, gboolean echo_on, const char *prompt_fmt, ...) @@ -1032,7 +1036,7 @@ nmc_readline_echo (const NmcConfig *nmc_config, rl_redisplay_function = nmc_secret_redisplay; } - str = nmc_readline_helper (nmc_config, prompt); + str = nmc_readline_helper (nmc_config, readline_status, prompt); /* Restore the non-hiding behavior */ if (!echo_on) { @@ -1196,101 +1200,40 @@ nmc_parse_lldp_capabilities (guint value) } static void -command_done (GObject *object, GAsyncResult *res, gpointer user_data) +nmc_complete_help (const char *prefix) { - GTask *task = G_TASK (res); - NmCli *nmc = user_data; - gs_free_error GError *error = NULL; - - if (!g_task_propagate_boolean (task, &error)) { - nmc->return_value = error->code; - g_string_assign (nmc->return_text, error->message); - } - - if (!nmc->should_wait) - g_main_loop_quit (loop); + nmc_complete_strings (prefix, "help"); + if (*prefix == '-') + nmc_complete_strings (prefix, "-help", "--help"); } -typedef struct { - const NMCCommand *cmd; - int argc; - char **argv; - GTask *task; -} CmdCall; - -static void -call_cmd (NmCli *nmc, GTask *task, const NMCCommand *cmd, int argc, char **argv); - static void got_client (GObject *source_object, GAsyncResult *res, gpointer user_data) { - gs_unref_object GTask *task = NULL; + gs_unref_object NMClient *client = NM_CLIENT (source_object); gs_free_error GError *error = NULL; - CmdCall *call = user_data; - NmCli *nmc; - - nm_assert (NM_IS_CLIENT (source_object)); + nm_auto_pop_run_status NmCli *nmc = NULL; + const NMCCommand *c; + gpointer argc_p; + int argc; + const char *const*argv; - task = g_steal_pointer (&call->task); - nmc = g_task_get_task_data (task); + nm_utils_user_data_unpack (user_data, &nmc, &c, &argc_p, &argv); - nmc->should_wait--; + argc = GPOINTER_TO_INT (argc_p); if (!g_async_initable_init_finish (G_ASYNC_INITABLE (source_object), res, &error)) { - g_object_unref (source_object); - g_task_return_new_error (task, NMCLI_ERROR, NMC_RESULT_ERROR_UNKNOWN, - _("Error: Could not create NMClient object: %s."), - error->message); - } else { - nmc->client = NM_CLIENT (source_object); - call_cmd (nmc, g_steal_pointer (&task), call->cmd, call->argc, call->argv); + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: Could not create NMClient object: %s."), + error->message); } - g_slice_free (CmdCall, call); -} - -static void -call_cmd (NmCli *nmc, GTask *task, const NMCCommand *cmd, int argc, char **argv) -{ - CmdCall *call; - - if (nmc->client || !cmd->needs_client) { - - /* Check whether NetworkManager is running */ - if (cmd->needs_nm_running && !nm_client_get_nm_running (nmc->client)) { - g_task_return_new_error (task, NMCLI_ERROR, NMC_RESULT_ERROR_NM_NOT_RUNNING, - _("Error: NetworkManager is not running.")); - } else { - nmc->return_value = cmd->func (nmc, argc, argv); - g_task_return_boolean (task, TRUE); - } - - g_object_unref (task); - } else { - nm_assert (nmc->client == NULL); - - nmc->should_wait++; - call = g_slice_new0 (CmdCall); - call->cmd = cmd; - call->argc = argc; - call->argv = argv; - call->task = task; - nmc_client_new_async (NULL, - got_client, - call, - NM_CLIENT_INSTANCE_FLAGS, (guint) NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS, - NULL); - } -} + nmc->client = g_steal_pointer (&client); -static void -nmc_complete_help (const char *prefix) -{ - nmc_complete_strings (prefix, "help"); - if (*prefix == '-') - nmc_complete_strings (prefix, "-help", "--help"); + c->func (c, nmc, argc, argv); } /** @@ -1313,26 +1256,30 @@ nmc_complete_help (const char *prefix) * no callback to free the memory in (for simplicity). */ void -nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char **argv) +nmc_do_cmd (NmCli *nmc, + const NMCCommand *cmds, + const char *cmd, + int argc, + const char *const*argv) { const NMCCommand *c; - gs_unref_object GTask *task = NULL; - task = nm_g_task_new (NULL, NULL, nmc_do_cmd, command_done, nmc); - g_task_set_task_data (task, nmc, NULL); + nmc_run_status_wait_push (&nmc->run_status); - if (argc == 0 && nmc->complete) { - g_task_return_boolean (task, TRUE); + if ( argc == 0 + && nmc->complete) { + nmc_run_status_return_success (&nmc->run_status); return; } - if (argc == 1 && nmc->complete) { + if ( argc == 1 + && nmc->complete) { for (c = cmds; c->cmd; ++c) { if (!*cmd || matches (cmd, c->cmd)) g_print ("%s\n", c->cmd); } nmc_complete_help (cmd); - g_task_return_boolean (task, TRUE); + nmc_run_status_return_success (&nmc->run_status); return; } @@ -1343,31 +1290,61 @@ nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char if (c->cmd) { /* A valid command was specified. */ - if (c->usage && argc == 2 && nmc->complete) - nmc_complete_help (*(argv+1)); - if (!nmc->complete && c->usage && nmc_arg_is_help (*(argv+1))) { + if ( c->usage + && argc == 2 + && nmc->complete) + nmc_complete_help (argv[1]); + if ( !nmc->complete + && c->usage + && nmc_arg_is_help (argv[1])) { c->usage (); - g_task_return_boolean (task, TRUE); - } else { - call_cmd (nmc, g_steal_pointer (&task), c, argc, argv); + nmc_run_status_return_success (&nmc->run_status); + return; } } else if (cmd) { /* Not a known command. */ if (nmc_arg_is_help (cmd) && c->usage) { c->usage (); - g_task_return_boolean (task, TRUE); - } else { - g_task_return_new_error (task, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: argument '%s' not understood. Try passing --help instead."), cmd); + nmc_run_status_return_success (&nmc->run_status); + return; } + + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: argument '%s' not understood. Try passing --help instead."), + cmd); + return; } else if (c->func) { /* No command, run the default handler. */ - call_cmd (nmc, g_steal_pointer (&task), c, argc, argv); } else { /* No command and no default handler. */ - g_task_return_new_error (task, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: missing argument. Try passing --help.")); + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: missing argument. Try passing --help.")); + return; + } + + if ( nmc->client + || !c->needs_client) { + + if ( c->needs_nm_running + && !nm_client_get_nm_running (nmc->client)) { + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_NM_NOT_RUNNING, + _("Error: NetworkManager is not running.")); + return; + } + + c->func (c, nmc, argc, argv); + return; } + + nmc_run_status_wait_push (&nmc->run_status); + nmc_client_new_async (nmc->run_status.cancellable, + got_client, + nm_utils_user_data_pack (nmc, cmd, GINT_TO_POINTER (argc), argv), + NM_CLIENT_INSTANCE_FLAGS, (guint) NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS, + NULL); } /** diff --git a/clients/cli/common.h b/clients/cli/common.h index f8fad8c673..26e245da66 100644 --- a/clients/cli/common.h +++ b/clients/cli/common.h @@ -44,17 +44,20 @@ char *nmc_unique_connection_name (const GPtrArray *connections, void nmc_cleanup_readline (void); char *nmc_readline (const NmcConfig *nmc_config, + NmcReadlineStatus *readline_status, const char *prompt_fmt, - ...) G_GNUC_PRINTF (2, 3); + ...) G_GNUC_PRINTF (3, 4); char *nmc_readline_echo (const NmcConfig *nmc_config, + NmcReadlineStatus *readline_status, gboolean echo_on, const char *prompt_fmt, - ...) G_GNUC_PRINTF (3, 4); + ...) G_GNUC_PRINTF (4, 5); NmcCompEntryFunc nmc_rl_compentry_func_wrap (const char *const*values); char *nmc_rl_gen_func_basic (const char *text, int state, const char *const*words); char *nmc_rl_gen_func_ifnames (const char *text, int state); -gboolean nmc_get_in_readline (void); -void nmc_set_in_readline (gboolean in_readline); + +gboolean nmc_get_in_readline (NmcReadlineStatus *readline_status); +void nmc_set_in_readline (NmcReadlineStatus *readline_status, gboolean in_readline); /* for pre-filling a string to readline prompt */ extern char *nmc_rl_pre_input_deftext; @@ -62,15 +65,11 @@ 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); - gboolean needs_client; - gboolean needs_nm_running; -} NMCCommand; - -void nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char **argv); +void nmc_do_cmd (NmCli *nmc, + const NMCCommand *cmds, + const char *cmd, + int argc, + const char *const*argv); void nmc_complete_strv (const char *prefix, gssize nargs, const char *const*args); diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 7fe22adf63..75795560cb 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -885,8 +885,6 @@ const NmcMetaGenericInfo *const nmc_fields_con_active_details_groups[] = { #define CON_SHOW_DETAIL_GROUP_PROFILE "profile" #define CON_SHOW_DETAIL_GROUP_ACTIVE "active" -static guint progress_id = 0; /* ID of event source for displaying progress */ - /* for readline TAB completion in editor */ typedef struct { NmCli *nmc; @@ -1223,14 +1221,6 @@ usage_connection_export (void) "The data are directed to standard output or to a file if a name is given.\n\n")); } -static void -quit (void) -{ - if (nm_clear_g_source (&progress_id)) - nmc_terminal_erase_line (); - g_main_loop_quit (loop); -} - static char * construct_header_name (const char *base, const char *spec) { @@ -1346,9 +1336,9 @@ update_secrets_in_connection (NMRemoteConnection *remote, NMConnection *local) static gboolean nmc_connection_profile_details (NMConnection *connection, NmCli *nmc) { - GError *error = NULL; - GArray *print_settings_array; - GPtrArray *prop_array = NULL; + gs_free_error GError *error = NULL; + gs_unref_array GArray *print_settings_array = NULL; + gs_unref_ptrarray GPtrArray *prop_array = NULL; guint i; char *fields_str; char *fields_all = NMC_FIELDS_SETTINGS_NAMES_ALL; @@ -1366,12 +1356,14 @@ nmc_connection_profile_details (NMConnection *connection, NmCli *nmc) print_settings_array = parse_output_fields (fields_str, (const NMMetaAbstractInfo *const*) nm_meta_setting_infos_editor_p (), TRUE, &prop_array, &error); if (error) { - g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message); - g_error_free (error); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'connection show': %s"), + error->message); return FALSE; } - g_assert (print_settings_array); + + nm_assert (print_settings_array); /* Main header */ { @@ -1417,10 +1409,6 @@ nmc_connection_profile_details (NMConnection *connection, NmCli *nmc) } } - g_array_free (print_settings_array, TRUE); - if (prop_array) - g_ptr_array_free (prop_array, TRUE); - return TRUE; } @@ -1440,9 +1428,9 @@ nmc_active_connection_state_to_color (NMActiveConnectionState state) static gboolean nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc) { - GError *error = NULL; - GArray *print_groups; - GPtrArray *group_fields = NULL; + gs_free_error GError *error = NULL; + gs_unref_array GArray *print_groups = NULL; + gs_unref_ptrarray GPtrArray *group_fields = NULL; int i; const char *fields_str = NULL; const char *base_hdr = _("Activate connection details"); @@ -1455,12 +1443,14 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc) print_groups = parse_output_fields (fields_str, (const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_groups, TRUE, &group_fields, &error); if (error) { - g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message); - g_error_free (error); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'connection show': %s"), + error->message); return FALSE; } - g_assert (print_groups); + + nm_assert (print_groups); /* Main header */ { @@ -1489,7 +1479,7 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc) /* Loop through the groups and print them. */ for (i = 0; i < print_groups->len; i++) { int group_idx = g_array_index (print_groups, int, i); - char *group_fld = (char *) g_ptr_array_index (group_fields, i); + char *group_fld = g_ptr_array_index (group_fields, i); if ( NM_IN_SET (nmc->nmc_config.print_output, NMC_PRINT_NORMAL, NMC_PRINT_PRETTY) && !nmc->nmc_config.multiline_output @@ -1566,10 +1556,6 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc) } } - g_array_free (print_groups, TRUE); - if (group_fields) - g_ptr_array_free (group_fields, TRUE); - return TRUE; } @@ -1976,7 +1962,7 @@ parse_preferred_connection_order (const char *order, GError **error) static NMConnection * get_connection (NmCli *nmc, int *argc, - char ***argv, + const char *const**argv, const char **out_selector, const char **out_value, GPtrArray **out_result, @@ -2027,8 +2013,8 @@ get_connection (NmCli *nmc, return connection; } -static NMCResultCode -do_connections_show (NmCli *nmc, int argc, char **argv) +static void +do_connections_show (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { gs_free_error GError *err = NULL; gs_free char *profile_flds = NULL; @@ -2132,9 +2118,10 @@ do_connections_show (NmCli *nmc, int argc, char **argv) /* Before printing the connections check if we have a "--show-secret" * option after the connection ids */ - if (!nmc->nmc_config.show_secrets && !nmc->complete) { + if ( !nmc->nmc_config.show_secrets + && !nmc->complete) { int argc_cp = argc; - char **argv_cp = argv; + const char *const*argv_cp = argv; do { if (NM_IN_STRSET (*argv_cp, "id", "uuid", "path", "filename", "apath")) { @@ -2162,9 +2149,11 @@ do_connections_show (NmCli *nmc, int argc, char **argv) argc--; argv++; if (!argc) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: %s argument is missing."), + argv[-1]); + return; } } @@ -2189,10 +2178,13 @@ do_connections_show (NmCli *nmc, int argc, char **argv) } } - if (!con && !explicit_acon) { - g_string_printf (nmc->return_text, _("Error: %s - no such connection profile."), *argv); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto finish; + if ( !con + && !explicit_acon) { + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_NOT_FOUND, + _("Error: %s - no such connection profile."), + *argv); + return; } /* Print connection details: @@ -2281,10 +2273,11 @@ do_connections_show (NmCli *nmc, int argc, char **argv) finish: if (err) { - g_string_printf (nmc->return_text, _("Error: %s."), err->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: %s."), + err->message); } - return nmc->return_value; } static NMActiveConnection * @@ -2453,10 +2446,11 @@ typedef struct { NmCli *nmc; NMDevice *device; NMActiveConnection *active; + GSource *progress_source; } ActivateConnectionInfo; static void -active_connection_hint (GString *return_text, +active_connection_hint (NmcRunStatus *run_status, NMActiveConnection *active, NMDevice *device) { @@ -2488,8 +2482,7 @@ active_connection_hint (GString *return_text, } } - g_string_append (return_text, "\n"); - g_string_append_printf (return_text, _("Hint: use '%s' to get more details."), hint->str); + nmc_run_status_return_append_message (run_status, _("Hint: use '%s' to get more details."), hint->str); } static void activate_connection_info_finish (ActivateConnectionInfo *info); @@ -2515,15 +2508,16 @@ check_activated (ActivateConnectionInfo *info) nm_object_get_path (NM_OBJECT (info->active))); } activate_connection_info_finish (info); - break; + return; case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED: nm_assert (reason); - g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"), - reason); - active_connection_hint (nmc->return_text, info->active, info->device); - nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_CON_ACTIVATION, + _("Error: Connection activation failed: %s"), + reason); + active_connection_hint (&nmc->run_status, info->active, info->device); activate_connection_info_finish (info); - break; + return; case NM_ACTIVE_CONNECTION_STATE_ACTIVATING: if (nmc->secret_agent) { NMRemoteConnection *connection = nm_active_connection_get_connection (info->active); @@ -2531,7 +2525,7 @@ check_activated (ActivateConnectionInfo *info) nm_secret_agent_simple_enable (nmc->secret_agent, nm_connection_get_path (NM_CONNECTION (connection))); } - break; + return; default: break; } @@ -2552,32 +2546,25 @@ active_connection_state_cb (NMActiveConnection *active, check_activated (info); } -static void -set_nmc_error_timeout (NmCli *nmc) -{ - g_string_printf (nmc->return_text, _("Error: Timeout expired (%d seconds)"), nmc->timeout); - nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED; -} - static gboolean activate_connection_timeout_cb (gpointer user_data) { ActivateConnectionInfo *info = user_data; + NmCli *nmc = info->nmc; - /* Time expired -> exit nmcli */ - set_nmc_error_timeout (info->nmc); + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_TIMEOUT_EXPIRED, + _("Error: Timeout expired (%d seconds)"), + nmc->timeout); activate_connection_info_finish (info); - return FALSE; + return G_SOURCE_REMOVE; } static gboolean progress_cb (gpointer user_data) { - const char *str = (const char *) user_data; - - nmc_terminal_show_progress (str); - - return TRUE; + nmc_terminal_show_progress (_("preparing")); + return G_SOURCE_CONTINUE; } static gboolean @@ -2596,9 +2583,8 @@ progress_active_connection_cb (gpointer user_data) * is more interesting. */ ac_devs = nm_active_connection_get_devices (active); device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL; - } else { + } else device = NULL; - } str = device ? gettext (nmc_device_state_to_string (nm_device_get_state (device))) @@ -2606,12 +2592,14 @@ progress_active_connection_cb (gpointer user_data) nmc_terminal_show_progress (str); - return TRUE; + return G_SOURCE_CONTINUE; } static void activate_connection_info_finish (ActivateConnectionInfo *info) { + NmCli *nmc = info->nmc; + if (info->device) { g_signal_handlers_disconnect_by_func (info->device, G_CALLBACK (device_state_cb), info); g_object_unref (info->device); @@ -2622,8 +2610,12 @@ activate_connection_info_finish (ActivateConnectionInfo *info) g_object_unref (info->active); } - g_free (info); - quit (); + nm_clear_g_source_inst (&info->progress_source); + + nm_g_slice_free (info); + + nmc_run_status_return_success_if_necessary (&nmc->run_status); + nmc_run_status_wait_pop (&nmc->run_status); } static void @@ -2635,58 +2627,63 @@ activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_dat NMActiveConnection *active; NMActiveConnectionState state; const GPtrArray *ac_devs; - GError *error = NULL; + gs_free_error GError *error = NULL; - info->active = active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error); + active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error); + info->active = active; if (error) { - g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"), - error->message); - g_error_free (error); - active_connection_hint (nmc->return_text, info->active, info->device); - nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_CON_ACTIVATION, + _("Error: Connection activation failed: %s"), + error->message); + active_connection_hint (&nmc->run_status, info->active, info->device); activate_connection_info_finish (info); - } else { - state = nm_active_connection_get_state (active); - if (!device && !nm_active_connection_get_vpn (active)) { - /* device could be NULL for virtual devices. Fill it here. */ - ac_devs = nm_active_connection_get_devices (active); - device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL; - if (device) - info->device = g_object_ref (device); - } + return; + } - if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { - /* User doesn't want to wait or already activated */ - if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { - if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) - nmc_terminal_erase_line (); - g_print (_("Connection successfully activated (D-Bus active path: %s)\n"), - nm_object_get_path (NM_OBJECT (active))); - } - activate_connection_info_finish (info); - } else { - /* Monitor the active connection and device (if available) states */ - g_signal_connect (active, "state-changed", G_CALLBACK (active_connection_state_cb), info); - if (device) - g_signal_connect (device, "notify::" NM_DEVICE_STATE, G_CALLBACK (device_state_cb), info); - /* Both active_connection_state_cb () and device_state_cb () will just - * call check_activated (info). So, just call it once directly after - * connecting on both the signals of the objects and skip the call to - * the callbacks. - */ - check_activated (info); + state = nm_active_connection_get_state (active); + if (!device && !nm_active_connection_get_vpn (active)) { + /* device could be NULL for virtual devices. Fill it here. */ + ac_devs = nm_active_connection_get_devices (active); + device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL; + if (device) + info->device = g_object_ref (device); + } - /* Start progress indication showing VPN states */ - if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) { - if (progress_id) - g_source_remove (progress_id); - progress_id = g_timeout_add (120, progress_active_connection_cb, active); - } + if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + /* User doesn't want to wait or already activated */ + if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + g_print (_("Connection successfully activated (D-Bus active path: %s)\n"), + nm_object_get_path (NM_OBJECT (active))); + } + activate_connection_info_finish (info); + } else { + /* Monitor the active connection and device (if available) states */ + g_signal_connect (active, "state-changed", G_CALLBACK (active_connection_state_cb), info); + if (device) + g_signal_connect (device, "notify::" NM_DEVICE_STATE, G_CALLBACK (device_state_cb), info); + /* Both active_connection_state_cb () and device_state_cb () will just + * call check_activated (info). So, just call it once directly after + * connecting on both the signals of the objects and skip the call to + * the callbacks. + */ + check_activated (info); - /* Start timer not to loop forever when signals are not emitted */ - g_timeout_add_seconds (nmc->timeout, activate_connection_timeout_cb, info); + /* Start progress indication showing VPN states */ + if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) { + nmc_run_status_wait_remove_source (&nmc->run_status, + g_steal_pointer (&info->progress_source)); + info->progress_source = g_source_ref (nmc_run_status_wait_add_timeout (&nmc->run_status, + 120, + progress_active_connection_cb, + active)); } + + /* Start timer not to loop forever when signals are not emitted */ + g_timeout_add_seconds (nmc->timeout, activate_connection_timeout_cb, info); } } @@ -2783,6 +2780,7 @@ nmc_activate_connection (NmCli *nmc, const char *nsp, const char *pwds, GAsyncReadyCallback callback, + GSource ***out_progress_source_location, GError **error) { ActivateConnectionInfo *info; @@ -2795,6 +2793,8 @@ nmc_activate_connection (NmCli *nmc, g_return_val_if_fail (nmc, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + NM_SET_OUT (out_progress_source_location, NULL); + if ( connection && ( ifname || ap @@ -2839,23 +2839,27 @@ nmc_activate_connection (NmCli *nmc, nmc); } - info = g_malloc0 (sizeof (ActivateConnectionInfo)); - info->nmc = nmc; - if (device) - info->device = g_object_ref (device); + info = g_slice_new (ActivateConnectionInfo); + *info = (ActivateConnectionInfo) { + .nmc = nmc, + .device = nm_g_object_ref (device), + }; nm_client_activate_connection_async (nmc->client, connection, device, spec_object, - NULL, + nmc->run_status.cancellable, callback, info); + + NM_SET_OUT (out_progress_source_location, &info->progress_source); + return TRUE; } -static NMCResultCode -do_connection_up (NmCli *nmc, int argc, char **argv) +static void +do_connection_up (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { NMConnection *connection = NULL; const char *ifname = NULL; @@ -2863,10 +2867,11 @@ do_connection_up (NmCli *nmc, int argc, char **argv) const char *nsp = NULL; const char *pwds = NULL; gs_free_error GError *error = NULL; - char **arg_arr = NULL; + gs_strfreev char **arg_arr = NULL; int arg_num; - char ***argv_ptr; + const char *const**argv_ptr; int *argc_ptr; + GSource **progress_source_location; /* * Set default timeout for connection activation. @@ -2879,7 +2884,8 @@ do_connection_up (NmCli *nmc, int argc, char **argv) argv_ptr = &argv; argc_ptr = &argc; - if (argc == 0 && nmc->ask) { + if ( argc == 0 + && nmc->ask) { gs_free char *line = NULL; /* nmc_do_cmd() should not call this with argc=0. */ @@ -2888,15 +2894,19 @@ do_connection_up (NmCli *nmc, int argc, char **argv) line = nmc_readline (&nmc->nmc_config, PROMPT_CONNECTION); nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); - argv_ptr = &arg_arr; + argv_ptr = (const char *const**) &arg_arr; argc_ptr = &arg_num; } - if (argc > 0 && strcmp (*argv, "ifname") != 0) { + if ( argc > 0 + && nm_streq (*argv, "ifname")) { connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, NULL, NULL, &error); if (!connection) { - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - return error->code; + nmc_run_status_return (&nmc->run_status, + error->code, + _("Error: %s."), + error->message); + return; } } @@ -2908,8 +2918,11 @@ do_connection_up (NmCli *nmc, int argc, char **argv) argc--; argv++; if (!argc) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - return NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: %s argument is missing."), + argv[-1]); + return; } ifname = *argv; @@ -2920,8 +2933,11 @@ do_connection_up (NmCli *nmc, int argc, char **argv) argc--; argv++; if (!argc) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - return NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: %s argument is missing."), + argv[-1]); + return; } ap = *argv; @@ -2932,45 +2948,63 @@ do_connection_up (NmCli *nmc, int argc, char **argv) argc--; argv++; if (!argc) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - return NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: %s argument is missing."), + argv[-1]); + return; } if (argc == 1 && nmc->complete) - nmc->return_value = NMC_RESULT_COMPLETE_FILE; + nmc_run_status_set_complete_file (&nmc->run_status); pwds = *argv; } else if (!nmc->complete) { - g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv); - return NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: invalid extra argument '%s'."), + *argv); + return; } next_arg (nmc, &argc, &argv, NULL); } if (nmc->complete) - return nmc->return_value; + return; /* 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. */ nmc->nowait_flag = (nmc->timeout == 0); - nmc->should_wait++; - 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->should_wait--; - return error->code; + if (!nmc_activate_connection (nmc, + connection, + ifname, + ap, + nsp, + pwds, + activate_connection_cb, + &progress_source_location, + &error)) { + nmc_run_status_return (&nmc->run_status, + error->code, + _("Error: %s."), + error->message); + return; } - /* Start progress indication */ - if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) - progress_id = g_timeout_add (120, progress_cb, _("preparing")); + nmc_run_status_wait_push (&nmc->run_status); - return nmc->return_value; + if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) { + /* Start progress indication */ + *progress_source_location = g_source_ref (nmc_run_status_wait_add_timeout (&nmc->run_status, + 120, + progress_cb, + NULL)); + } } /*****************************************************************************/ @@ -2980,7 +3014,7 @@ typedef struct { /* a list of object that is relevant for the callback. The object * type differs, and depends on the type of callback. */ GPtrArray *obj_list; - guint timeout_id; + GSource *timeout_source; GCancellable *cancellable; } ConnectionCbInfo; @@ -3044,6 +3078,8 @@ connection_cb_info_obj_list_steal (ConnectionCbInfo *info, gpointer obj) static void connection_cb_info_finish (ConnectionCbInfo *info, gpointer obj) { + NmCli *nmc = info->nmc; + if (obj) { obj = connection_cb_info_obj_list_steal (info, obj); if (obj) @@ -3059,73 +3095,193 @@ connection_cb_info_finish (ConnectionCbInfo *info, gpointer obj) if (info->obj_list->len > 0) return; - nm_clear_g_source (&info->timeout_id); + //XXX +#if 0 + if (info->timeout_source) { + nmc_run_status_wait_remove_source (nmc, g_steal_pointer (&info->timeout_source)); + nmc_run_status_wait_pop (&nmc->run_status); + } +#endif + nm_clear_g_cancellable (&info->cancellable); g_ptr_array_free (info->obj_list, TRUE); g_signal_handlers_disconnect_by_func (info->nmc->client, connection_removed_cb, info); - g_slice_free (ConnectionCbInfo, info); + nm_g_slice_free (info); - quit (); + nmc_run_status_return_success_if_necessary (&nmc->run_status); + nmc_run_status_wait_pop (&nmc->run_status); } /*****************************************************************************/ +struct _ConnectionDownData; + +typedef struct { + struct _ConnectionDownData *info; + NMActiveConnection *active; + char *id; + gulong signal_id; + bool done:1; +} ConnectionDownItem; + +typedef struct _ConnectionDownData { + NmCli *nmc; + GSource *timeout_source; + char *not_found_ac; + char *deactivation_failure; + ConnectionDownItem *items; + guint n_items; + guint timeout_sec; +} ConnectionDownData; + static void -connection_removed_cb (NMClient *client, NMConnection *connection, ConnectionCbInfo *info) +_down_connection_finish (ConnectionDownData *info) { - if (!connection_cb_info_obj_list_has (info, connection)) - return; - g_print (_("Connection '%s' (%s) successfully deleted.\n"), - nm_connection_get_id (connection), - nm_connection_get_uuid (connection)); - connection_cb_info_finish (info, connection); + nm_auto_pop_run_status NmCli *nmc = info->nmc; + guint i; + + if (!nmc_run_status_is_returned (&nmc->run_status)) { + if (info->not_found_ac) { + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_NOT_FOUND, + _("Error: '%s' is not an active connection."), + info->not_found_ac); + } if (info->deactivation_failure) { + nmc_run_status_return_message_full (&nmc->run_status, + NMC_RESULT_ERROR_CON_DEACTIVATION, + g_steal_pointer (&info->deactivation_failure)); + } else + nmc_run_status_return_success (&nmc->run_status); + } + + g_free (info->not_found_ac); + g_free (info->deactivation_failure); + for (i = 0; i < info->n_items; i++) { + ConnectionDownItem *item = &info->items[i]; + + nm_clear_g_signal_handler (item->active, &item->signal_id); + g_object_unref (item->active); + g_free (item->id); + } + g_free (info->items); + if (info->timeout_source) + nmc_run_status_wait_remove_source (&nmc->run_status, info->timeout_source); + + nm_g_slice_free (info); } static void -down_active_connection_state_cb (NMActiveConnection *active, - GParamSpec *pspec, - ConnectionCbInfo *info) +_down_connection_check_done (ConnectionDownItem *item) { - if (nm_active_connection_get_state (active) < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) + ConnectionDownData *info = item->info; + guint i; + + if ( nm_object_get_client (NM_OBJECT (item->active)) + && nm_active_connection_get_state (item->active) < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) return; - if (info->nmc->nmc_config.print_output == NMC_PRINT_PRETTY) - nmc_terminal_erase_line (); - g_print (_("Connection '%s' successfully deactivated (D-Bus active path: %s)\n"), - nm_active_connection_get_id (active), nm_object_get_path (NM_OBJECT (active))); + nm_clear_g_signal_handler (item->active, &item->signal_id); + + if (!item->done) { + item->done = TRUE; + g_print (_("Connection '%s' successfully deactivated (D-Bus active path: %s)\n"), + item->id, + nm_object_get_path (NM_OBJECT (item->active))); + } + + for (i = 0; i < info->n_items; i++) { + if (!info->items[i].done) + return; + } - g_signal_handlers_disconnect_by_func (G_OBJECT (active), - down_active_connection_state_cb, - info); - connection_cb_info_finish (info, active); + _down_connection_finish (info); +} + +static void +_down_connection_ac_state_change (NMActiveConnection *active, + GParamSpec *pspec, + ConnectionDownItem *item) +{ + _down_connection_check_done (item); } static gboolean -connection_op_timeout_cb (gpointer user_data) +_down_connection_timeout_cb (gpointer user_data) { - ConnectionCbInfo *info = user_data; + ConnectionDownData *info = user_data; + NmCli *nmc = info->nmc; + guint i; - set_nmc_error_timeout (info->nmc); - connection_cb_info_finish (info, NULL); + for (i = 0; i < info->n_items; i++) { + ConnectionDownItem *item = &info->items[i]; + + if (!item->done) { + g_print (_("Connection '%s' deactivation failed: timeout reached\n"), + item->id); + } + } + + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_TIMEOUT_EXPIRED, + _("Error: Timeout expired (%u seconds)"), + info->timeout_sec); + _down_connection_finish (info); return G_SOURCE_REMOVE; } -static NMCResultCode -do_connection_down (NmCli *nmc, int argc, char **argv) +static void +_down_connection_cb (GObject *source, GAsyncResult *result, gpointer user_data) +{ + nm_auto_pop_run_status NmCli *nmc = NULL; + ConnectionDownItem *item; + ConnectionDownData *info; + gs_free_error GError *error = NULL; + gboolean success; + + nm_utils_user_data_unpack (user_data, &nmc, &item); + + success = nm_client_deactivate_connection_finish (nmc->client, + result, + &error); + + if (nm_utils_error_is_cancelled (error)) + return; + + item = user_data; + info = item->info; + + if (!success) { + item->done = TRUE; + + g_print (_("Connection '%s' deactivation failed: %s\n"), + item->id, error->message); + + if (!info->deactivation_failure) + info->deactivation_failure = g_strdup_printf (_("Failure to deactivate connect '%s'"), item->id); + } else { + item->signal_id = g_signal_connect (item->active, + "notify::" NM_ACTIVE_CONNECTION_STATE, + G_CALLBACK (_down_connection_ac_state_change), + item); + } + + _down_connection_check_done (item); +} + +static void +do_connection_down (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { NMActiveConnection *active; - ConnectionCbInfo *info = NULL; const GPtrArray *active_cons; gs_strfreev char **arg_arr = NULL; - char **arg_ptr; + const char *const*arg_ptr; int arg_num; guint i; gs_unref_ptrarray GPtrArray *found_active_cons = NULL; - - if (nmc->timeout == -1) - nmc->timeout = 10; + const char *not_found_ac = NULL; + ConnectionDownData *info; next_arg (nmc, &argc, &argv, NULL); arg_ptr = argv; @@ -3141,11 +3297,13 @@ do_connection_down (NmCli *nmc, int argc, char **argv) line = nmc_readline (&nmc->nmc_config, PROMPT_ACTIVE_CONNECTIONS); nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); - arg_ptr = arg_arr; + arg_ptr = (const char *const*) arg_arr; } if (arg_num == 0) { - g_string_printf (nmc->return_text, _("Error: No connection specified.")); - return NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: No connection specified.")); + return; } } @@ -3162,8 +3320,11 @@ do_connection_down (NmCli *nmc, int argc, char **argv) arg_num--; arg_ptr++; if (!arg_num) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); - return NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: %s argument is missing."), + selector); + return; } } @@ -3175,59 +3336,63 @@ do_connection_down (NmCli *nmc, int argc, char **argv) if (!active) { 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; + if (!not_found_ac) + not_found_ac = *arg_ptr; } next_arg (nmc->ask ? NULL : nmc, &arg_num, &arg_ptr, NULL); } if (!found_active_cons) { - g_string_printf (nmc->return_text, _("Error: no active connection provided.")); - return NMC_RESULT_ERROR_NOT_FOUND; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_NOT_FOUND, + _("Error: no active connection provided.")); + return; } + nm_assert (found_active_cons->len > 0); if (nmc->complete) - return nmc->return_value; + return; - if (nmc->timeout > 0) { - nmc->should_wait++; + nmc_run_status_wait_push (&nmc->run_status); - info = g_slice_new0 (ConnectionCbInfo); - info->nmc = nmc; - info->obj_list = g_ptr_array_sized_new (found_active_cons->len); - for (i = 0; i < found_active_cons->len; i++) { - active = found_active_cons->pdata[i]; - g_ptr_array_add (info->obj_list, g_object_ref (active)); - g_signal_connect (active, - "notify::" NM_ACTIVE_CONNECTION_STATE, - G_CALLBACK (down_active_connection_state_cb), - info); - } - info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info); + info = g_slice_new (ConnectionDownData); + *info = (ConnectionDownData) { + .nmc = nmc, + .not_found_ac = g_strdup (not_found_ac), + .timeout_sec = nmc->timeout == -1 + ? 10u + : nmc->timeout, + }; + + if ( info->timeout_sec != 0u + && info->timeout_sec < G_MAXUINT / 1000u) { + info->timeout_source = g_source_ref (nmc_run_status_wait_add_timeout (&nmc->run_status, + info->timeout_sec * 1000u, + _down_connection_timeout_cb, + info)); } + info->items = g_new (ConnectionDownItem, found_active_cons->len); + info->n_items = found_active_cons->len; + for (i = 0; i < found_active_cons->len; i++) { - GError *error = NULL; + ConnectionDownItem *item = &info->items[i]; - active = found_active_cons->pdata[i]; + *item = (ConnectionDownItem) { + .info = info, + .active = g_object_ref (found_active_cons->pdata[i]), + .id = g_strdup (nm_active_connection_get_id (active) ?: "???"), + }; - if (!nm_client_deactivate_connection (nmc->client, active, NULL, &error)) { - g_print (_("Connection '%s' deactivation failed: %s\n"), - nm_active_connection_get_id (active), error->message); - g_clear_error (&error); - - if (info) { - g_signal_handlers_disconnect_by_func (active, - down_active_connection_state_cb, - info); - connection_cb_info_finish (info, active); - } - } + nmc_run_status_wait_push (&nmc->run_status); + nm_client_deactivate_connection_async (nmc->client, + active, + nmc->run_status.cancellable, + _down_connection_cb, + nm_utils_user_data_pack (nmc, item)); } - - return nmc->return_value; } /*****************************************************************************/ @@ -3715,7 +3880,10 @@ prompt_yes_no (gboolean default_yes, char *delim) } static NMSetting * -is_setting_valid (NMConnection *connection, const NMMetaSettingValidPartItem *const*valid_settings_main, const NMMetaSettingValidPartItem *const*valid_settings_slave, char *setting) +is_setting_valid (NMConnection *connection, + const NMMetaSettingValidPartItem *const*valid_settings_main, + const NMMetaSettingValidPartItem *const*valid_settings_slave, + const char *setting) { const char *setting_name; @@ -4656,7 +4824,7 @@ complete_option (NmCli *nmc, const NMMetaAbstractInfo *abstract_info, const char &complete_filename, &values_to_free); if (complete_filename) { - nmc->return_value = NMC_RESULT_COMPLETE_FILE; + nmc_run_status_set_complete_file (&nmc->run_status); return TRUE; } if (values) { @@ -4726,7 +4894,7 @@ connection_remove_setting (NMConnection *connection, NMSetting *setting, GError } static gboolean -get_value (const char **value, int *argc, char ***argv, const char *option, GError **error) +get_value (const char **value, int *argc, const char *const**argv, const char *option, GError **error) { if (!**argv) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -4749,7 +4917,7 @@ gboolean nmc_process_connection_properties (NmCli *nmc, NMConnection *connection, int *argc, - char ***argv, + const char *const**argv, gboolean allow_setting_removal, GError **error) { @@ -4789,7 +4957,7 @@ nmc_process_connection_properties (NmCli *nmc, && modifier == NM_META_ACCESSOR_MODIFIER_SET && nm_streq (option, "remove")) { NMSetting *ss; - char *setting_name; + const char *setting_name; (*argc)--; (*argv)++; @@ -4961,49 +5129,48 @@ add_connection_cb (GObject *client, gpointer user_data) { nm_auto_free_add_connection_info AddConnectionInfo *info = user_data; - NmCli *nmc = info->nmc; - NMRemoteConnection *connection; - GError *error = NULL; + nm_auto_pop_run_status NmCli *nmc = info->nmc; + gs_unref_object NMRemoteConnection *connection = NULL; + gs_free_error GError *error = NULL; const GPtrArray *connections; guint i, found; connection = nm_client_add_connection2_finish (NM_CLIENT (client), result, NULL, &error); if (error) { - g_string_printf (nmc->return_text, - _("Error: Failed to add '%s' connection: %s"), - info->new_id, error->message); - g_error_free (error); - nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; - } else { - connections = nm_client_get_connections (nmc->client); - if (connections) { - found = 0; - for (i = 0; i < connections->len; i++) { - NMConnection *candidate = NM_CONNECTION (connections->pdata[i]); + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_CON_ACTIVATION, + _("Error: Failed to add '%s' connection: %s"), + info->new_id, + error->message); + return; + } - if ((NMConnection *) connection == candidate) - continue; - if (nm_streq0 (nm_connection_get_id (candidate), info->new_id)) - found++; - } - if (found > 0) { - g_printerr (g_dngettext (GETTEXT_PACKAGE, - "Warning: There is another connection with the name '%1$s'. Reference the connection by its uuid '%2$s'\n", - "Warning: There are %3$u other connections with the name '%1$s'. Reference the connection by its uuid '%2$s'\n", - found), - info->new_id, - nm_connection_get_uuid (NM_CONNECTION (connection)), - found); - } - } + connections = nm_client_get_connections (nmc->client); + if (connections) { + found = 0; + for (i = 0; i < connections->len; i++) { + NMConnection *candidate = NM_CONNECTION (connections->pdata[i]); - g_print (_("Connection '%s' (%s) successfully added.\n"), - nm_connection_get_id (NM_CONNECTION (connection)), - nm_connection_get_uuid (NM_CONNECTION (connection))); - g_object_unref (connection); + if ((NMConnection *) connection == candidate) + continue; + if (nm_streq0 (nm_connection_get_id (candidate), info->new_id)) + found++; + } + if (found > 0) { + g_printerr (g_dngettext (GETTEXT_PACKAGE, + "Warning: There is another connection with the name '%1$s'. Reference the connection by its uuid '%2$s'\n", + "Warning: There are %3$u other connections with the name '%1$s'. Reference the connection by its uuid '%2$s'\n", + found), + info->new_id, + nm_connection_get_uuid (NM_CONNECTION (connection)), + found); + } } - quit (); + g_print (_("Connection '%s' (%s) successfully added.\n"), + nm_connection_get_id (NM_CONNECTION (connection)), + nm_connection_get_uuid (NM_CONNECTION (connection))); + nmc_run_status_return_success (&nmc->run_status); } static void @@ -5348,8 +5515,8 @@ again: return TRUE; } -static NMCResultCode -do_connection_add (NmCli *nmc, int argc, char **argv) +static void +do_connection_add (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { gs_unref_object NMConnection *connection = NULL; NMSettingConnection *s_con; @@ -5362,8 +5529,6 @@ do_connection_add (NmCli *nmc, int argc, char **argv) rl_attempted_completion_function = nmcli_con_add_tab_completion; - nmc->return_value = NMC_RESULT_SUCCESS; - connection = nm_simple_connection_new (); s_con = (NMSettingConnection *) nm_setting_connection_new (); @@ -5372,7 +5537,8 @@ do_connection_add (NmCli *nmc, int argc, char **argv) read_properties: g_clear_error (&error); /* Get the arguments from the command line if any */ - if (argc && !nmc_process_connection_properties (nmc, connection, &argc, &argv, FALSE, &error)) { + if ( argc + && !nmc_process_connection_properties (nmc, connection, &argc, &argv, FALSE, &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 "--" */ @@ -5385,25 +5551,27 @@ read_properties: argc--; argv++; if (!argc) { - g_string_printf (nmc->return_text, - _("Error: value for '%s' argument is required."), - "save"); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: value for '%s' argument is required."), + "save"); goto finish; } g_clear_error (&error); 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; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'save': %s."), + error->message); goto finish; } next_arg (nmc, &argc, &argv, NULL); goto read_properties; } - g_string_assign (nmc->return_text, error->message); - nmc->return_value = error->code; + nmc_run_status_return_message (&nmc->run_status, + error->code, + error->message); goto finish; } @@ -5478,8 +5646,10 @@ read_properties: if (!option_relevant (connection, (const NMMetaAbstractInfo *) bi)) continue; if (bi->base.inf_flags & NM_META_PROPERTY_INF_FLAG_REQD) { - g_string_printf (nmc->return_text, _("Error: '%s' argument is required."), bi->base.property_alias); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s' argument is required."), + bi->base.property_alias); goto finish; } } @@ -5489,24 +5659,25 @@ read_properties: if (!option_relevant (connection, (const NMMetaAbstractInfo *) property_info)) continue; if (property_info->inf_flags & NM_META_PROPERTY_INF_FLAG_REQD) { - g_string_printf (nmc->return_text, _("Error: '%s' argument is required."), property_info->property_alias); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s' argument is required."), + property_info->property_alias); goto finish; } } } } + nmc_run_status_wait_push (&nmc->run_status); add_connection (nmc->client, connection, !save_bool, add_connection_cb, _add_connection_info_new (nmc, NULL, connection)); - nmc->should_wait++; finish: reset_options (); - return nmc->return_value; } /*****************************************************************************/ @@ -8113,8 +8284,15 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t nmc->nowait_flag = FALSE; nmc->should_wait++; nmc->nmc_config_mutable.print_output = NMC_PRINT_PRETTY; - if (!nmc_activate_connection (nmc, NM_CONNECTION (rem_con), ifname, ap_nsp, ap_nsp, NULL, - activate_connection_editor_cb, &tmp_err)) { + if (!nmc_activate_connection (nmc, + NM_CONNECTION (rem_con), + ifname, + ap_nsp, + ap_nsp, + NULL, + activate_connection_editor_cb, + NULL, + &tmp_err)) { g_print (_("Error: Cannot activate connection: %s.\n"), tmp_err->message); g_clear_error (&tmp_err); break; @@ -8744,6 +8922,31 @@ delete_cb (GObject *con, GAsyncResult *result, gpointer user_data) } } +static gboolean +connection_op_timeout_cb (gpointer user_data) +{ + ConnectionCbInfo *info = user_data; + NmCli *nmc = info->nmc; + + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_TIMEOUT_EXPIRED, + _("Error: Timeout expired (%d seconds)"), + nmc->timeout); + connection_cb_info_finish (info, NULL); + return G_SOURCE_REMOVE; +} + +static void +connection_removed_cb (NMClient *client, NMConnection *connection, ConnectionCbInfo *info) +{ + if (!connection_cb_info_obj_list_has (info, connection)) + return; + g_print (_("Connection '%s' (%s) successfully deleted.\n"), + nm_connection_get_id (connection), + nm_connection_get_uuid (connection)); + connection_cb_info_finish (info, connection); +} + static NMCResultCode do_connection_delete (NmCli *nmc, int argc, char **argv) { @@ -9053,7 +9256,7 @@ do_connection_import (NmCli *nmc, int argc, char **argv) argc--; argv++; if (!argc) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), argv[-1]); return NMC_RESULT_ERROR_USER_INPUT; } @@ -9074,7 +9277,7 @@ do_connection_import (NmCli *nmc, int argc, char **argv) argc--; argv++; if (!argc) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), argv[-1]); return NMC_RESULT_ERROR_USER_INPUT; } if (argc == 1 && nmc->complete) @@ -9129,14 +9332,12 @@ do_connection_import (NmCli *nmc, int argc, char **argv) return NMC_RESULT_ERROR_UNKNOWN; } + nmc_run_status_wait_push (&nmc->run_status); add_connection (nmc->client, connection, temporary, add_connection_cb, _add_connection_info_new (nmc, NULL, connection)); - nmc->should_wait++; - - return nmc->return_value; } static NMCResultCode @@ -9343,20 +9544,20 @@ nmcli_con_tab_completion (const char *text, int start, int end) } static const NMCCommand connection_cmds[] = { - { "show", do_connections_show, usage_connection_show, TRUE, TRUE }, - { "up", do_connection_up, usage_connection_up, TRUE, TRUE }, - { "down", do_connection_down, usage_connection_down, TRUE, TRUE }, - { "add", do_connection_add, usage_connection_add, TRUE, TRUE }, - { "edit", do_connection_edit, usage_connection_edit, TRUE, TRUE }, - { "delete", do_connection_delete, usage_connection_delete, TRUE, TRUE }, - { "reload", do_connection_reload, usage_connection_reload, FALSE, FALSE }, - { "load", do_connection_load, usage_connection_load, TRUE, TRUE }, - { "modify", do_connection_modify, usage_connection_modify, TRUE, TRUE }, - { "clone", do_connection_clone, usage_connection_clone, TRUE, TRUE }, - { "import", do_connection_import, usage_connection_import, TRUE, TRUE }, - { "export", do_connection_export, usage_connection_export, TRUE, TRUE }, - { "monitor", do_connection_monitor, usage_connection_monitor, TRUE, TRUE }, - { NULL, do_connections_show, usage, TRUE, TRUE }, + { "show", do_connections_show, usage_connection_show, TRUE, TRUE }, + { "up", do_connection_up, usage_connection_up, TRUE, TRUE }, + { "down", do_connection_down, usage_connection_down, TRUE, TRUE }, + { "add", do_connection_add, usage_connection_add, TRUE, TRUE }, + { "edit", do_connection_edit, usage_connection_edit, TRUE, TRUE }, + { "delete", do_connection_delete, usage_connection_delete, TRUE, TRUE }, + { "reload", do_connection_reload, usage_connection_reload, FALSE, FALSE }, + { "load", do_connection_load, usage_connection_load, TRUE, TRUE }, + { "modify", do_connection_modify, usage_connection_modify, TRUE, TRUE }, + { "clone", do_connection_clone, usage_connection_clone, TRUE, TRUE }, + { "import", do_connection_import, usage_connection_import, TRUE, TRUE }, + { "export", do_connection_export, usage_connection_export, TRUE, TRUE }, + { "monitor", do_connection_monitor, usage_connection_monitor, TRUE, TRUE }, + { NULL, do_connections_show, usage, TRUE, TRUE }, }; /* Entry point function for connections-related commands: 'nmcli connection' */ diff --git a/clients/cli/connections.h b/clients/cli/connections.h index b419ba9c00..bfc2a4f908 100644 --- a/clients/cli/connections.h +++ b/clients/cli/connections.h @@ -16,7 +16,7 @@ gboolean nmc_process_connection_properties (NmCli *nmc, NMConnection *connection, int *argc, - char ***argv, + const char *const**argv, gboolean allow_remove_setting, GError **error); diff --git a/clients/cli/general.c b/clients/cli/general.c index 6e7410ed20..05f37678ca 100644 --- a/clients/cli/general.c +++ b/clients/cli/general.c @@ -5,8 +5,6 @@ #include "nm-default.h" -#include "general.h" - #include <stdlib.h> #include "nm-libnm-core-intern/nm-common-macros.h" @@ -457,12 +455,6 @@ usage_monitor (void) } static void -quit (void) -{ - g_main_loop_quit (loop); -} - -static gboolean show_nm_status (NmCli *nmc, const char *pretty_header_name, const char *print_flds) { gs_free_error GError *error = NULL; @@ -484,43 +476,43 @@ show_nm_status (NmCli *nmc, const char *pretty_header_name, const char *print_fl (const NMMetaAbstractInfo *const*) metagen_general_status, fields_str, &error)) { - g_string_printf (nmc->return_text, _("Error: only these fields are allowed: %s"), fields_all); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return FALSE; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: only these fields are allowed: %s"), + fields_all); } - return TRUE; } -static NMCResultCode -do_general_status (NmCli *nmc, int argc, char **argv) +static void +do_general_status (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { next_arg (nmc, &argc, &argv, NULL); if (nmc->complete) - return nmc->return_value; + return; show_nm_status (nmc, NULL, NULL); - return nmc->return_value; } static gboolean timeout_cb (gpointer user_data) { - NmCli *nmc = (NmCli *) user_data; + nm_auto_pop_run_status NmCli *nmc = user_data; g_signal_handlers_disconnect_by_func (nmc->client, G_CALLBACK (permission_changed), nmc); - g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout); - nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED; - quit (); - return FALSE; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_TIMEOUT_EXPIRED, + _("Error: Timeout %d sec expired."), + nmc->timeout); + return G_SOURCE_REMOVE; } static void -print_permissions (void *user_data) +print_permissions (NmCli *nmc) { - NmCli *nmc = user_data; + nm_auto_pop_run_status NmCli *nmc_pop_run_status = NULL; gs_free_error GError *error = NULL; const char *fields_str = NULL; gpointer permissions[G_N_ELEMENTS (nm_auth_permission_sorted) + 1]; @@ -539,11 +531,13 @@ print_permissions (void *user_data) G_CALLBACK (permission_changed), nmc); + nmc_pop_run_status = nmc; + if (!is_running) { /* NetworkManager quit while we were waiting. */ - g_string_printf (nmc->return_text, _("NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - quit (); + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_NM_NOT_RUNNING, + _("NetworkManager is not running.")); return; } @@ -565,11 +559,11 @@ print_permissions (void *user_data) (const NMMetaAbstractInfo *const*) metagen_general_permissions, fields_str, &error)) { - g_string_printf (nmc->return_text, _("Error: 'general permissions': %s"), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'general permissions': %s"), + error->message); } - - quit (); } static void @@ -582,36 +576,8 @@ permission_changed (GObject *gobject, print_permissions (nmc); } -static gboolean -show_nm_permissions (NmCli *nmc) -{ - NMClientInstanceFlags instance_flags; - - instance_flags = nm_client_get_instance_flags (nmc->client); - instance_flags &= ~NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS; - - g_object_set (nmc->client, - NM_CLIENT_INSTANCE_FLAGS, (guint) instance_flags, - NULL); - - g_signal_connect (nmc->client, - "notify", - G_CALLBACK (permission_changed), - nmc); - - if (nmc->timeout == -1) - nmc->timeout = 10; - g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); - - nmc->should_wait++; - - print_permissions (nmc); - - return TRUE; -} - -static NMCResultCode -do_general_reload (NmCli *nmc, int argc, char **argv) +static void +do_general_reload (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { gs_unref_variant GVariant *result = NULL; gs_free_error GError *error = NULL; @@ -624,7 +590,7 @@ do_general_reload (NmCli *nmc, int argc, char **argv) if (nmc->complete) { if (argc == 0) - return nmc->return_value; + return; if (argc == 1) { values = nm_utils_enum_get_values (nm_manager_reload_flags_get_type (), @@ -632,7 +598,7 @@ do_general_reload (NmCli *nmc, int argc, char **argv) NM_MANAGER_RELOAD_FLAG_ALL); nmc_complete_strv (*argv, -1, values); } - return nmc->return_value; + return; } if (argc > 0) { @@ -641,21 +607,26 @@ do_general_reload (NmCli *nmc, int argc, char **argv) NM_MANAGER_RELOAD_FLAG_CONF, NM_MANAGER_RELOAD_FLAG_ALL); joined = g_strjoinv (",", (char **) values); - g_string_printf (nmc->return_text, - _("Error: invalid reload flag '%s'. Allowed flags are: %s"), - err_token, - joined); - return NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: invalid reload flag '%s'. Allowed flags are: %s"), + err_token, + joined); + return; } argc--; argv++; } if (argc > 0) { - g_string_printf (nmc->return_text, _("Error: extra argument '%s'"), *argv); - return NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: extra argument '%s'"), + *argv); + return; } + /* FIXME(cli-sync) */ result = nmc_dbus_call_sync (nmc, "/org/freedesktop/NetworkManager", "org.freedesktop.NetworkManager", @@ -665,53 +636,45 @@ do_general_reload (NmCli *nmc, int argc, char **argv) &error); if (error) { - g_string_printf (nmc->return_text, - _("Error: failed to reload: %s"), - nmc_error_get_simple_message (error)); - return NMC_RESULT_ERROR_UNKNOWN; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_UNKNOWN, + _("Error: failed to reload: %s"), + nmc_error_get_simple_message (error)); + return; } - - return nmc->return_value; } -static NMCResultCode -do_general_permissions (NmCli *nmc, int argc, char **argv) +static void +do_general_permissions (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { + NMClientInstanceFlags instance_flags; + next_arg (nmc, &argc, &argv, NULL); if (nmc->complete) - return nmc->return_value; + return; - show_nm_permissions (nmc); - return nmc->return_value; -} + instance_flags = nm_client_get_instance_flags (nmc->client); + instance_flags &= ~NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS; -static void -show_general_logging (NmCli *nmc) -{ - gs_free char *level_cache = NULL; - gs_free char *domains_cache = NULL; - gs_free_error GError *error = NULL; - const char *fields_str = NULL; - GetGeneralLoggingData d = { - .level = &level_cache, - .domains = &domains_cache, - }; + g_object_set (nmc->client, + NM_CLIENT_INSTANCE_FLAGS, (guint) instance_flags, + NULL); - if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "common") == 0) { - } else if (g_ascii_strcasecmp (nmc->required_fields, "all") == 0) { - } else - fields_str = nmc->required_fields; + g_signal_connect (nmc->client, + "notify", + G_CALLBACK (permission_changed), + nmc); - if (!nmc_print (&nmc->nmc_config, - (gpointer const []) { &d, NULL }, - NULL, - _("NetworkManager logging"), - (const NMMetaAbstractInfo *const*) metagen_general_logging, - fields_str, - &error)) { - g_string_printf (nmc->return_text, _("Error: 'general logging': %s"), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } + nmc_run_status_wait_add_timeout (&nmc->run_status, + nmc->timeout == -1 + ? 10 + : nmc->timeout, + timeout_cb, + nmc); + + nmc_run_status_wait_push (&nmc->run_status); + + print_permissions (nmc); } static void @@ -734,237 +697,283 @@ nmc_complete_strings_nocase (const char *prefix, ...) static void _set_logging_cb (GObject *object, GAsyncResult *result, gpointer user_data) { - NmCli *nmc = user_data; + nm_auto_pop_run_status NmCli *nmc = user_data; gs_unref_variant GVariant *res = NULL; gs_free_error GError *error = NULL; res = nm_client_dbus_call_finish (NM_CLIENT (object), result, &error); if (!res) { + if (nm_utils_error_is_cancelled (error)) + return; g_dbus_error_strip_remote_error (error); - g_string_printf (nmc->return_text, _("Error: failed to set logging: %s"), - nmc_error_get_simple_message (error)); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_UNKNOWN, + _("Error: failed to set logging: %s"), + nmc_error_get_simple_message (error)); } - quit (); } -static NMCResultCode -do_general_logging (NmCli *nmc, int argc, char **argv) +static void +do_general_logging (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { + const char *level = NULL; + const char *domains = NULL; + next_arg (nmc, &argc, &argv, NULL); + if (argc == 0) { + gs_free char *level_cache = NULL; + gs_free char *domains_cache = NULL; + gs_free_error GError *error = NULL; + const char *fields_str = NULL; + GetGeneralLoggingData d = { + .level = &level_cache, + .domains = &domains_cache, + }; + if (nmc->complete) - return nmc->return_value; + return; - show_general_logging (nmc); - } else { - /* arguments provided -> set logging level and domains */ - const char *level = NULL; - const char *domains = NULL; - - do { - if (argc == 1 && nmc->complete) - nmc_complete_strings (*argv, "level", "domains"); - - if (matches (*argv, "level")) { - argc--; - argv++; - if (!argc) { - g_string_printf (nmc->return_text, _("Error: '%s' argument is missing."), *(argv-1)); - return NMC_RESULT_ERROR_USER_INPUT; - } - if (argc == 1 && nmc->complete) { - nmc_complete_strings_nocase (*argv, "TRACE", "DEBUG", "INFO", "WARN", - "ERR", "OFF", "KEEP", NULL); - } - level = *argv; - } else if (matches (*argv, "domains")) { - argc--; - argv++; - if (!argc) { - g_string_printf (nmc->return_text, _("Error: '%s' argument is missing."), *(argv-1)); - return NMC_RESULT_ERROR_USER_INPUT; - } - if (argc == 1 && nmc->complete) { - nmc_complete_strings_nocase (*argv, "PLATFORM", "RFKILL", "ETHER", "WIFI", "BT", - "MB", "DHCP4", "DHCP6", "PPP", "WIFI_SCAN", "IP4", - "IP6", "AUTOIP4", "DNS", "VPN", "SHARING", "SUPPLICANT", - "AGENTS", "SETTINGS", "SUSPEND", "CORE", "DEVICE", "OLPC", - "INFINIBAND", "FIREWALL", "ADSL", "BOND", "VLAN", "BRIDGE", - "DBUS_PROPS", "TEAM", "CONCHECK", "DCB", "DISPATCH", "AUDIT", - "SYSTEMD", "VPN_PLUGIN", "PROXY", "TC", NULL); - } - domains = *argv; - } else { - g_string_printf (nmc->return_text, _("Error: property '%s' is not known."), *argv); - return NMC_RESULT_ERROR_USER_INPUT; + if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "common") == 0) { + } else if (g_ascii_strcasecmp (nmc->required_fields, "all") == 0) { + } else + fields_str = nmc->required_fields; + + if (!nmc_print (&nmc->nmc_config, + (gpointer const []) { &d, NULL }, + NULL, + _("NetworkManager logging"), + (const NMMetaAbstractInfo *const*) metagen_general_logging, + fields_str, + &error)) { + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'general logging': %s"), + error->message); + } + return; + } + + /* arguments provided -> set logging level and domains */ + do { + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "level", "domains"); + + if (matches (*argv, "level")) { + argc--; + argv++; + if (!argc) { + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s' argument is missing."), + argv[-1]); + return; } - } while (next_arg (nmc, &argc, &argv, NULL) == 0); + if (argc == 1 && nmc->complete) { + nmc_complete_strings_nocase (*argv, "TRACE", "DEBUG", "INFO", "WARN", + "ERR", "OFF", "KEEP", NULL); + } + level = *argv; + continue; + } - if (nmc->complete) - return nmc->return_value; - - nmc->should_wait++; - nm_client_dbus_call (nmc->client, - NM_DBUS_PATH, - NM_DBUS_INTERFACE, - "SetLogging", - g_variant_new ("(ss)", - level ?: "", - domains ?: ""), - G_VARIANT_TYPE ("()"), - -1, - NULL, - _set_logging_cb, - nmc); - } + if (matches (*argv, "domains")) { + argc--; + argv++; + if (!argc) { + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s' argument is missing."), + argv[-1]); + return; + } + if (argc == 1 && nmc->complete) { + nmc_complete_strings_nocase (*argv, "PLATFORM", "RFKILL", "ETHER", "WIFI", "BT", + "MB", "DHCP4", "DHCP6", "PPP", "WIFI_SCAN", "IP4", + "IP6", "AUTOIP4", "DNS", "VPN", "SHARING", "SUPPLICANT", + "AGENTS", "SETTINGS", "SUSPEND", "CORE", "DEVICE", "OLPC", + "INFINIBAND", "FIREWALL", "ADSL", "BOND", "VLAN", "BRIDGE", + "DBUS_PROPS", "TEAM", "CONCHECK", "DCB", "DISPATCH", "AUDIT", + "SYSTEMD", "VPN_PLUGIN", "PROXY", "TC", NULL); + } + domains = *argv; + continue; + } + + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: property '%s' is not known."), + argv[0]); + return; + } while (next_arg (nmc, &argc, &argv, NULL) == 0); + + if (nmc->complete) + return; - return nmc->return_value; + nmc_run_status_wait_push (&nmc->run_status); + nm_client_dbus_call (nmc->client, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "SetLogging", + g_variant_new ("(ss)", + level ?: "", + domains ?: ""), + G_VARIANT_TYPE ("()"), + -1, + nmc->run_status.cancellable, + _set_logging_cb, + nmc); } static void save_hostname_cb (GObject *object, GAsyncResult *result, gpointer user_data) { - NmCli *nmc = (NmCli *) user_data; - GError *error = NULL; + nm_auto_pop_run_status NmCli *nmc = user_data; + gs_free_error GError *error = NULL; nm_client_save_hostname_finish (NM_CLIENT (object), result, &error); if (error) { - g_string_printf (nmc->return_text, _("Error: failed to set hostname: %s"), - error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (error); + if (nm_utils_error_is_cancelled (error)) + return; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_UNKNOWN, + _("Error: failed to set hostname: %s"), + error->message); } - quit (); } -static NMCResultCode -do_general_hostname (NmCli *nmc, int argc, char **argv) +static void +do_general_hostname (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { + const char *hostname; + next_arg (nmc, &argc, &argv, NULL); if (nmc->complete) - return nmc->return_value; + return; if (argc == 0) { - /* no arguments -> get hostname */ - char *hostname = NULL; - - 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; + gs_free char *h = NULL; - if (next_arg (nmc, &argc, &argv, NULL) == 0) - g_print ("Warning: ignoring extra garbage after '%s' hostname\n", hostname); - - nmc->should_wait++; - nm_client_save_hostname_async (nmc->client, hostname, NULL, save_hostname_cb, nmc); + g_object_get (nmc->client, NM_CLIENT_HOSTNAME, &h, NULL); + if (h) + g_print ("%s\n", h); + return; } - return nmc->return_value; + hostname = *argv; + if (next_arg (nmc, &argc, &argv, NULL) == 0) + g_print ("Warning: ignoring extra garbage after '%s' hostname\n", hostname); + nmc_run_status_wait_push (&nmc->run_status); + nm_client_save_hostname_async (nmc->client, hostname, nmc->run_status.cancellable, save_hostname_cb, nmc); } -static const NMCCommand general_cmds[] = { - { "status", do_general_status, usage_general_status, TRUE, TRUE }, - { "hostname", do_general_hostname, usage_general_hostname, TRUE, TRUE }, - { "permissions", do_general_permissions, usage_general_permissions, TRUE, TRUE }, - { "logging", do_general_logging, usage_general_logging, TRUE, TRUE }, - { "reload", do_general_reload, usage_general_reload, FALSE, FALSE }, - { NULL, do_general_status, usage_general, TRUE, TRUE }, -}; - -/* - * Entry point function for general operations 'nmcli general' - */ -NMCResultCode -do_general (NmCli *nmc, int argc, char **argv) +void +nmc_command_do_general (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { + static const NMCCommand general_cmds[] = { + { "status", do_general_status, usage_general_status, TRUE, TRUE }, + { "hostname", do_general_hostname, usage_general_hostname, TRUE, TRUE }, + { "permissions", do_general_permissions, usage_general_permissions, TRUE, TRUE }, + { "logging", do_general_logging, usage_general_logging, TRUE, TRUE }, + { "reload", do_general_reload, usage_general_reload, FALSE, FALSE }, + { NULL, do_general_status, usage_general, TRUE, TRUE }, + }; + next_arg (nmc, &argc, &argv, NULL); - /* Register polkit agent */ nmc_start_polkit_agent_start_try (nmc); nmc_do_cmd (nmc, general_cmds, *argv, argc, argv); - - return nmc->return_value; } -static gboolean +static void nmc_switch_show (NmCli *nmc, const char *switch_name, const char *header) { - g_return_val_if_fail (nmc != NULL, FALSE); - g_return_val_if_fail (switch_name != NULL, FALSE); - - if (nmc->required_fields && g_ascii_strcasecmp (nmc->required_fields, switch_name) != 0) { - g_string_printf (nmc->return_text, _("Error: '--fields' value '%s' is not valid here (allowed field: %s)"), - nmc->required_fields, switch_name); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return FALSE; + nm_assert (nmc); + nm_assert (switch_name); + + if ( nmc->required_fields + && g_ascii_strcasecmp (nmc->required_fields, switch_name) != 0) { + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: '--fields' value '%s' is not valid here (allowed field: %s)"), + nmc->required_fields, + switch_name); + return; } + if (nmc->nmc_config.print_output == NMC_PRINT_NORMAL) nmc->nmc_config_mutable.print_output = NMC_PRINT_TERSE; if (!nmc->required_fields) nmc->required_fields = g_strdup (switch_name); - return show_nm_status (nmc, header, NULL); + show_nm_status (nmc, header, NULL); } static gboolean nmc_switch_parse_on_off (NmCli *nmc, const char *arg1, const char *arg2, gboolean *res) { + int v; + g_return_val_if_fail (nmc != NULL, FALSE); g_return_val_if_fail (arg1 && arg2, FALSE); g_return_val_if_fail (res != NULL, FALSE); - if (!strcmp (arg2, "on")) - *res = TRUE; - else if (!strcmp (arg2, "off")) - *res = FALSE; - else { - g_string_printf (nmc->return_text, _("Error: invalid '%s' argument: '%s' (use on/off)."), arg1, arg2); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + v = _nm_utils_ascii_str_to_bool (arg2, -1); + if (v == -1) { + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: invalid '%s' argument: '%s' (use on/off)."), + arg1, + arg2); return FALSE; } + *res = v; return TRUE; } static void -_do_networking_on_off_cb (GObject *object, GAsyncResult *result, gpointer user_data) +_do_networking_onoff_cb (GObject *object, GAsyncResult *result, gpointer user_data) { - NmCli *nmc = user_data; + nm_auto_pop_run_status NmCli *nmc = user_data; gs_unref_variant GVariant *ret = NULL; gs_free_error GError *error = NULL; ret = nm_client_dbus_call_finish (NM_CLIENT (object), result, &error); if (!ret) { + if (nm_utils_error_is_cancelled (error)) + return; + if (g_error_matches (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED)) { /* This is fine. Be quiet about it. */ } else { g_dbus_error_strip_remote_error (error); - g_string_printf (nmc->return_text, _("Error: failed to set networking: %s"), - nmc_error_get_simple_message (error)); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_UNKNOWN, + _("Error: failed to set networking: %s"), + nmc_error_get_simple_message (error)); } } - quit (); } -static NMCResultCode -do_networking_on_off (NmCli *nmc, int argc, char **argv, gboolean enable) +static void +do_networking_onoff (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { + gboolean enable = nm_streq (cmd->cmd, "on"); + + next_arg (nmc, &argc, &argv, NULL); + if (nmc->complete) - return nmc->return_value; + return; nmc_start_polkit_agent_start_try (nmc); - nmc->should_wait++; + nmc_run_status_wait_push (&nmc->run_status); + nm_client_dbus_call (nmc->client, NM_DBUS_PATH, NM_DBUS_INTERFACE, @@ -972,223 +981,212 @@ do_networking_on_off (NmCli *nmc, int argc, char **argv, gboolean enable) g_variant_new ("(b)", enable), G_VARIANT_TYPE ("()"), -1, - NULL, - _do_networking_on_off_cb, + nmc->run_status.cancellable, + _do_networking_onoff_cb, nmc); - - return nmc->return_value; } -static NMCResultCode -do_networking_on (NmCli *nmc, int argc, char **argv) +static void +do_networking_connectivity (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { - next_arg (nmc, &argc, &argv, NULL); - return do_networking_on_off (nmc, argc, argv, TRUE); -} + gs_free_error GError *error = NULL; -static NMCResultCode -do_networking_off (NmCli *nmc, int argc, char **argv) -{ next_arg (nmc, &argc, &argv, NULL); - return do_networking_on_off (nmc, argc, argv, FALSE); -} -static NMCResultCode -do_networking_connectivity (NmCli *nmc, int argc, char **argv) -{ - next_arg (nmc, &argc, &argv, NULL); if (nmc->complete) { if (argc == 1) nmc_complete_strings (*argv, "check"); - return nmc->return_value; + return; } if (!argc) { /* no arguments -> get current state */ nmc_switch_show (nmc, NMC_FIELDS_NM_CONNECTIVITY, N_("Connectivity")); - } else if (matches (*argv, "check")) { - gs_free_error GError *error = NULL; - - /* Register polkit agent */ - nmc_start_polkit_agent_start_try (nmc); + return; + } - nm_client_check_connectivity (nmc->client, NULL, &error); - if (error) { - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - } else - nmc_switch_show (nmc, NMC_FIELDS_NM_CONNECTIVITY, N_("Connectivity")); - } else { + if (!matches (*argv, "check")) { usage_networking (); - g_string_printf (nmc->return_text, _("Error: 'networking' command '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'networking' command '%s' is not valid."), + *argv); + return; + } + + nmc_start_polkit_agent_start_try (nmc); + + /* FIXME(cli-sync) */ + nm_client_check_connectivity (nmc->client, nmc->run_status.cancellable, &error); + + if (error) { + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_UNKNOWN, + _("Error: %s."), + error->message); + return; } - return nmc->return_value; + nmc_switch_show (nmc, NMC_FIELDS_NM_CONNECTIVITY, N_("Connectivity")); } -static NMCResultCode -do_networking_show (NmCli *nmc, int argc, char **argv) +static void +do_networking_show (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { next_arg (nmc, &argc, &argv, NULL); + if (nmc->complete) - return nmc->return_value; + return; nmc_switch_show (nmc, NMC_FIELDS_NM_NETWORKING, N_("Networking")); - - return nmc->return_value; } -static const NMCCommand networking_cmds[] = { - { "on", do_networking_on, usage_networking_on, TRUE, TRUE }, - { "off", do_networking_off, usage_networking_off, TRUE, TRUE }, - { "connectivity", do_networking_connectivity, usage_networking_connectivity, TRUE, TRUE }, - { NULL, do_networking_show, usage_networking, TRUE, TRUE }, -}; - -/* - * Entry point function for networking commands 'nmcli networking' - */ -NMCResultCode -do_networking (NmCli *nmc, int argc, char **argv) +void +nmc_command_do_networking (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { + static const NMCCommand networking_cmds[] = { + { "on", do_networking_onoff, usage_networking_on, TRUE, TRUE }, + { "off", do_networking_onoff, usage_networking_off, TRUE, TRUE }, + { "connectivity", do_networking_connectivity, usage_networking_connectivity, TRUE, TRUE }, + { NULL, do_networking_show, usage_networking, TRUE, TRUE }, + }; + next_arg (nmc, &argc, &argv, NULL); nmc_do_cmd (nmc, networking_cmds, *argv, argc, argv); - - return nmc->return_value; } -static NMCResultCode -do_radio_all (NmCli *nmc, int argc, char **argv) +static void +do_radio_all (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { gboolean enable_flag; next_arg (nmc, &argc, &argv, NULL); + if (argc == 0) { if (nmc->complete) - return nmc->return_value; + return; /* no argument, show all radio switches */ show_nm_status (nmc, N_("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; + return; + } - 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 (nmc->complete) { + if (argc == 1) + nmc_complete_bool (*argv); + return; } - return nmc->return_value; + if (!nmc_switch_parse_on_off (nmc, argv[-1], *argv, &enable_flag)) + return; + + /* FIXME(cli-sync) */ + 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); } static void _do_radio_wifi_cb (GObject *object, GAsyncResult *result, gpointer user_data) { - NmCli *nmc = user_data; + nm_auto_pop_run_status NmCli *nmc = user_data; gs_free_error GError *error = NULL; if (!nm_client_dbus_set_property_finish (NM_CLIENT (object), result, &error)) { + if (nm_utils_error_is_cancelled (error)) + return; + g_dbus_error_strip_remote_error (error); - g_string_printf (nmc->return_text, _("Error: failed to set Wi-Fi radio: %s"), - nmc_error_get_simple_message (error)); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_UNKNOWN, + _("Error: failed to set Wi-Fi radio: %s"), + nmc_error_get_simple_message (error)); } - quit (); } -static NMCResultCode -do_radio_wifi (NmCli *nmc, int argc, char **argv) +static void +do_radio_wifi (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { gboolean enable_flag; next_arg (nmc, &argc, &argv, NULL); + if (argc == 0) { if (nmc->complete) - return nmc->return_value; + return; /* no argument, show current Wi-Fi state */ nmc_switch_show (nmc, NMC_FIELDS_NM_WIFI, N_("Wi-Fi 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_start_polkit_agent_start_try (nmc); - - nmc->should_wait++; - nm_client_dbus_set_property (nmc->client, - NM_DBUS_PATH, - NM_DBUS_INTERFACE, - "WirelessEnabled", - g_variant_new_boolean (enable_flag), - -1, - NULL, - _do_radio_wifi_cb, - nmc); + return; + } + + if (nmc->complete) { + if (argc == 1) + nmc_complete_bool (*argv); + return; } - return nmc->return_value; + if (!nmc_switch_parse_on_off (nmc, argv[-1], *argv, &enable_flag)) + return; + + nmc_start_polkit_agent_start_try (nmc); + + nmc_run_status_wait_push (&nmc->run_status); + nm_client_dbus_set_property (nmc->client, + NM_DBUS_PATH, + NM_DBUS_INTERFACE, + "WirelessEnabled", + g_variant_new_boolean (enable_flag), + -1, + nmc->run_status.cancellable, + _do_radio_wifi_cb, + nmc); } -static NMCResultCode -do_radio_wwan (NmCli *nmc, int argc, char **argv) +static void +do_radio_wwan (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { gboolean enable_flag; next_arg (nmc, &argc, &argv, NULL); if (argc == 0) { if (nmc->complete) - return nmc->return_value; + return; /* no argument, show current WWAN (mobile broadband) state */ nmc_switch_show (nmc, NMC_FIELDS_NM_WWAN, N_("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; + return; + } - nm_client_wwan_set_enabled (nmc->client, enable_flag); + if (nmc->complete) { + if (argc == 1) + nmc_complete_bool (*argv); + return; } - return nmc->return_value; -} + if (!nmc_switch_parse_on_off (nmc, argv[-1], *argv, &enable_flag)) + return; -static const NMCCommand radio_cmds[] = { - { "all", do_radio_all, usage_radio_all, TRUE, TRUE }, - { "wifi", do_radio_wifi, usage_radio_wifi, TRUE, TRUE }, - { "wwan", do_radio_wwan, usage_radio_wwan, TRUE, TRUE }, - { NULL, do_radio_all, usage_radio, TRUE, TRUE }, -}; + /* FIXME(cli-sync) */ + nm_client_wwan_set_enabled (nmc->client, enable_flag); +} -/* - * Entry point function for radio switch commands 'nmcli radio' - */ -NMCResultCode -do_radio (NmCli *nmc, int argc, char **argv) +void +nmc_command_do_radio (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { + static const NMCCommand radio_cmds[] = { + { "all", do_radio_all, usage_radio_all, TRUE, TRUE }, + { "wifi", do_radio_wifi, usage_radio_wifi, TRUE, TRUE }, + { "wwan", do_radio_wwan, usage_radio_wwan, TRUE, TRUE }, + { NULL, do_radio_all, usage_radio, TRUE, TRUE }, + }; + next_arg (nmc, &argc, &argv, NULL); - /* Register polkit agent */ nmc_start_polkit_agent_start_try (nmc); nmc_do_cmd (nmc, radio_cmds, *argv, argc, argv); - - return nmc->return_value; } static void @@ -1428,11 +1426,8 @@ ac_overview (NmCli *nmc, NMActiveConnection *ac) g_string_free (outbuf, TRUE); } -/* - * Entry point function for 'nmcli' without arguments. - */ -NMCResultCode -do_overview (NmCli *nmc, int argc, char **argv) +void +nmc_command_do_overview (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { NMDevice **devices; const GPtrArray *p; @@ -1444,7 +1439,6 @@ do_overview (NmCli *nmc, int argc, char **argv) next_arg (nmc, &argc, &argv, NULL); - /* Register polkit agent */ nmc_start_polkit_agent_start_try (nmc); nm_cli_spawn_pager (&nmc->nmc_config, &nmc->pager_data); @@ -1542,38 +1536,33 @@ do_overview (NmCli *nmc, int argc, char **argv) "\"nmcli connection show\" to get an overview on active connection profiles.\n" "\n" "Consult nmcli(1) and nmcli-examples(7) 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) +void +nmc_command_do_monitor (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv) { next_arg (nmc, &argc, &argv, NULL); if (nmc->complete) - return nmc->return_value; + return; if (argc > 0) { if (!nmc_arg_is_help (*argv)) { - g_string_printf (nmc->return_text, _("Error: 'monitor' command '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: 'monitor' command '%s' is not valid."), + *argv); } - usage_monitor (); - return nmc->return_value; + return; } if (!nm_client_get_nm_running (nmc->client)) { - char *str; + gs_free char *str = NULL; str = nmc_colorize (&nmc->nmc_config, NM_META_COLOR_MANAGER_STOPPED, _("Networkmanager is not running (waiting for it)\n")); g_print ("%s", str); - g_free (str); } g_signal_connect (nmc->client, "notify::" NM_CLIENT_NM_RUNNING, @@ -1587,10 +1576,6 @@ do_monitor (NmCli *nmc, int argc, char **argv) g_signal_connect (nmc->client, "notify::" NM_CLIENT_STATE, G_CALLBACK (client_state), nmc); - nmc->should_wait++; - monitor_devices (nmc); monitor_connections (nmc); - - return NMC_RESULT_SUCCESS; } diff --git a/clients/cli/general.h b/clients/cli/general.h deleted file mode 100644 index dd63e43f72..0000000000 --- a/clients/cli/general.h +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2010 - 2014 Red Hat, Inc. - */ - -#ifndef NMC_GENERAL_H -#define NMC_GENERAL_H - -#include "nmcli.h" - -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.c b/clients/cli/nmcli.c index 08d3ab16e0..551c8d2987 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -19,14 +19,13 @@ #include <readline/history.h> #include "nm-client-utils.h" +#include "nm-glib-aux/nm-c-list.h" #include "polkit-agent.h" #include "utils.h" #include "common.h" #include "connections.h" #include "devices.h" -#include "general.h" -#include "agent.h" #include "settings.h" #if defined(NM_DIST_VERSION) @@ -75,7 +74,11 @@ static NmCli nm_cli = { .client = NULL, - .return_value = NMC_RESULT_SUCCESS, + .run_status = { + ._priv = { + .sources_lst_head = C_LIST_INIT (nm_cli.run_status._priv.sources_lst_head), + }, + }, .timeout = -1, @@ -83,7 +86,6 @@ static NmCli nm_cli = { .pwds_hash = NULL, .pk_listener = NULL, - .should_wait = 0, .nowait_flag = TRUE, .nmc_config.print_output = NMC_PRINT_NORMAL, .nmc_config.multiline_output = FALSE, @@ -106,6 +108,76 @@ const NmCli *const nmc_meta_environment_arg = &nm_cli; /*****************************************************************************/ +void +nmc_run_status_return_message_full (NmcRunStatus *run_status, + NMCResultCode result_code, + char *result_message_take) +{ + nm_assert (run_status); + nm_assert (!run_status->_priv.is_returned); + nm_assert (run_status->_priv.result_code == 0); + nm_assert (!run_status->_priv.result_message); + + g_cancellable_cancel (run_status->cancellable); + + run_status->_priv.is_returned = TRUE; + run_status->_priv.result_code = result_code; + run_status->_priv.result_message = result_message_take; +} + +void +_nmc_run_status_signal_finished (NmcRunStatus *run_status) +{ + nm_assert (run_status); + nm_assert (!run_status->_priv.is_finished); + + if (!run_status->_priv.is_returned) + nmc_run_status_return_success (run_status); + + run_status->_priv.is_finished = TRUE; + nm_c_list_elem_free_all (&run_status->_priv.sources_lst_head, + (GDestroyNotify) nm_g_source_destroy_and_unref); +} + +GSource * +nmc_run_status_wait_add_timeout_full (NmcRunStatus *run_status, + guint timeout_msec, + GSourceFunc func, + gpointer user_data, + GDestroyNotify destroy_notify) +{ + GSource *source; + + nm_assert (run_status); + nm_assert (func); + + source = nm_g_timeout_source_new (timeout_msec, + G_PRIORITY_DEFAULT, + func, + user_data, + destroy_notify); + g_source_attach (source, NULL); + + c_list_link_tail (&run_status->_priv.sources_lst_head, + &nm_c_list_elem_new_stale (source)->lst); + + return source; +} + +void +nmc_run_status_wait_remove_source (NmcRunStatus *run_status, + GSource *source_take) +{ + nm_auto_unref_gsource GSource *source = g_steal_pointer (&source_take); + NMCListElem *elem; + + elem = nm_c_list_elem_find_first (&run_status->_priv.sources_lst_head, d, d == source); + if (elem) + nm_c_list_elem_free_full (elem, (GDestroyNotify) nm_g_source_destroy_and_unref); +} + +/*****************************************************************************/ + typedef struct { NmCli *nmc; int argc; @@ -113,7 +185,6 @@ typedef struct { } ArgsInfo; /* --- Global variables --- */ -GMainLoop *loop = NULL; struct termios termios_orig; NM_CACHED_QUARK_FCN ("nmcli-error-quark", nmcli_error_quark) @@ -258,26 +329,22 @@ usage (void) "\n")); } -static const NMCCommand nmcli_cmds[] = { - { "general", do_general, NULL, FALSE, FALSE }, - { "monitor", do_monitor, NULL, TRUE, FALSE }, - { "networking", do_networking, NULL, FALSE, FALSE }, - { "radio", do_radio, NULL, FALSE, FALSE }, - { "connection", do_connections, NULL, FALSE, FALSE }, - { "device", do_devices, NULL, FALSE, FALSE }, - { "agent", do_agent, NULL, FALSE, FALSE }, - { NULL, do_overview, usage, TRUE, TRUE }, -}; - static gboolean -matches_arg (NmCli *nmc, int *argc, char ***argv, const char *pattern, char **arg) +matches_arg (NmCli *nmc, + int *argc, + const char *const**argv, + const char *pattern, + char **arg) { - char *opt = *argv[0]; + gs_free char *opt_free = NULL; + const char *opt = (*argv)[0]; + gs_free char *arg_tmp = NULL; + const char *s; - if (nmc->return_value != NMC_RESULT_SUCCESS) { - /* Don't process further matches if there has been an error. */ - return FALSE; - } + nm_assert (!opt); + nm_assert (opt[0] == '-'); + nm_assert (!arg || !*arg); + nm_assert (!nmc_run_status_is_returned (&nmc->run_status)); if (opt[1] == '-') { /* We know one '-' was already seen by the caller. @@ -288,33 +355,33 @@ matches_arg (NmCli *nmc, int *argc, char ***argv, const char *pattern, char **ar if (arg) { /* If there's a "=" separator, replace it with NUL so that matches() * works and consider the part after it to be the arguemnt's value. */ - *arg = strchr (opt, '='); - if (*arg) { - **arg = '\0'; - (*arg)++; + s = strchr (opt, '='); + if (s) { + opt = nm_strndup_a (300, opt, s - opt, &opt_free); + arg_tmp = g_strdup (&s[1]); } } - if (!matches (opt, pattern)) { - if (arg && *arg) { - /* Back off the replacement of "=". */ - (*arg)--; - **arg = '='; - } + if (!matches (opt, pattern)) return FALSE; - } - if (arg && !*arg) { - /* We need a value, but the option didn't contain a "=<value>" part. - * Proceed to the next argument. */ - (*argc)--; - (*argv)++; - if (!*argc) { - g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return FALSE; + if (arg) { + if (arg_tmp) + *arg = g_steal_pointer (&arg_tmp); + else { + /* We need a value, but the option didn't contain a "=<value>" part. + * Proceed to the next argument. */ + if (*argc <= 1) { + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: missing argument for '%s' option."), + opt); + return FALSE; + } + (*argc)--; + (*argv)++; + *arg = g_strdup (*argv[0]); } - *arg = *argv[0]; } return TRUE; @@ -699,32 +766,152 @@ set_colors (NmcColorOption color_option, /*************************************************************************************/ +gboolean +nmc_seen_sigint (NmcReadlineStatus *readline_status) +{ + return readline_status->seen_sigint; +} + +void +nmc_clear_sigint (NmcReadlineStatus *readline_status) +{ + readline_status->seen_sigint = FALSE; +} + +static void +nmc_set_sigint (NmcReadlineStatus *readline_status) +{ + readline_status->seen_sigint = TRUE; +} + +void nmc_exit (void) +{ + tcsetattr (STDIN_FILENO, TCSADRAIN, &termios_orig); + nmc_cleanup_readline (); + exit (1); +} + +typedef struct { + int signo; + NmCli *nmc; +} SignalHandlerData; + +static SignalHandlerData * +signal_handler_data_new (NmCli *nmc, int signo) +{ + SignalHandlerData *d; + + d = g_new (SignalHandlerData, 1); + *d = (SignalHandlerData) { + .nmc = nmc, + .signo = signo, + }; + return d; +} + static gboolean -process_command_line (NmCli *nmc, int argc, char **argv) +signal_handler (gpointer user_data) { + SignalHandlerData *data = user_data; + NmCli *nmc = data->nmc; + int signo = data->signo; + + switch (signo) { + case SIGINT: + if (nmc_get_in_readline (&nmc->readline_status)) { + nmc_set_sigint (&nmc->readline_status); + } else { + nmc_run_status_return (&nmc->run_status, + 0x80 + signo, + _("Error: nmcli terminated by signal %s (%d)"), + strsignal (signo), + signo); + } + break; + case SIGTERM: + nmc_run_status_return (&nmc->run_status, + 0x80 + signo, + _("Error: nmcli terminated by signal %s (%d)"), + strsignal (signo), + signo); + break; + } + + return G_SOURCE_CONTINUE; +} + +void +nm_cli_spawn_pager (const NmcConfig *nmc_config, + NmcPagerData *pager_data) +{ + if (pager_data->pid != 0) + return; + pager_data->pid = nmc_terminal_spawn_pager (nmc_config); +} + +int +main (int argc_orig, char **argv_orig) +{ + static const NMCCommand nmcli_cmds[] = { + { "general", nmc_command_do_general, NULL, FALSE, FALSE }, + { "monitor", nmc_command_do_monitor, NULL, TRUE, FALSE }, + { "networking", nmc_command_do_networking, NULL, FALSE, FALSE }, + { "radio", nmc_command_do_radio, NULL, FALSE, FALSE }, + { "connection", nmc_command_do_connection, NULL, FALSE, FALSE }, + { "device", nmc_command_do_device, NULL, FALSE, FALSE }, + { "agent", nmc_command_do_agent, NULL, FALSE, FALSE }, + { NULL, nmc_command_do_overview, usage, TRUE, TRUE }, + }; NmcColorOption colors = NMC_USE_COLOR_AUTO; - char *base; + gs_free const char **argv_clone1 = NULL; + const char *base; + pid_t ret; + NmCli *nmc = &nm_cli; + guint signal_id_term; + guint signal_id_int; + int argc = argc_orig; + const char *const*argv = (const char *const*) argv_orig; + + /* Set locale to use environment variables */ + setlocale (LC_ALL, ""); + +#ifdef GETTEXT_PACKAGE + /* Set i18n stuff */ + bindtextdomain (GETTEXT_PACKAGE, NMLOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); +#endif + + signal_id_term = g_unix_signal_add_full (G_PRIORITY_DEFAULT, SIGTERM, signal_handler, signal_handler_data_new (nmc, SIGTERM), g_free); + signal_id_int = g_unix_signal_add_full (G_PRIORITY_DEFAULT, SIGINT, signal_handler, signal_handler_data_new (nmc, SIGINT), g_free); + + /* Save terminal settings */ + tcgetattr (STDIN_FILENO, &termios_orig); base = strrchr (argv[0], '/'); - if (base == NULL) + if (!base) base = argv[0]; else base++; - if (argc > 1 && nm_streq (argv[1], "--complete-args")) { - nmc->complete = TRUE; - argv[1] = argv[0]; - next_arg (nmc, &argc, &argv, NULL); + + arg_move_next (&argc, &argv); + + if ( argc >= 1 + && nm_streq (argv[0], "--complete-args")) { + nmc->complete_mutable = TRUE; + arg_move_next (&argc, &argv); } - next_arg (nmc, &argc, &argv, NULL); - /* parse options */ - while (argc) { - char *value; + nmc->run_status.cancellable_mutable = g_cancellable_new (); + + while (argc > 0) { + gs_free char *value = NULL; if (argv[0][0] != '-') break; - if (argc == 1 && nmc->complete) { + if ( argc == 1 + && nmc->complete) { nmc_complete_strings (argv[0], "--terse", "--pretty", "--mode", "--overview", "--colors", "--escape", "--fields", "--nocheck", "--get-values", @@ -741,27 +928,31 @@ process_command_line (NmCli *nmc, int argc, char **argv) nmc->nmc_config_mutable.overview = TRUE; } else if (matches_arg (nmc, &argc, &argv, "-terse", NULL)) { if (nmc->nmc_config.print_output == NMC_PRINT_TERSE) { - g_string_printf (nmc->return_text, _("Error: Option '--terse' is specified the second time.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return FALSE; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: Option '--terse' is specified the second time.")); + break; } else if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) { - g_string_printf (nmc->return_text, _("Error: Option '--terse' is mutually exclusive with '--pretty'.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return FALSE; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: Option '--terse' is mutually exclusive with '--pretty'.")); + break; } else nmc->nmc_config_mutable.print_output = NMC_PRINT_TERSE; } else if (matches_arg (nmc, &argc, &argv, "-pretty", NULL)) { if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) { - g_string_printf (nmc->return_text, _("Error: Option '--pretty' is specified the second time.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return FALSE; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: Option '--pretty' is specified the second time.")); + break; } else if (nmc->nmc_config.print_output == NMC_PRINT_TERSE) { - g_string_printf (nmc->return_text, _("Error: Option '--pretty' is mutually exclusive with '--terse'.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return FALSE; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: Option '--pretty' is mutually exclusive with '--terse'.")); + break; } else nmc->nmc_config_mutable.print_output = NMC_PRINT_PRETTY; @@ -774,9 +965,10 @@ process_command_line (NmCli *nmc, int argc, char **argv) else if (matches (value, "multiline")) nmc->nmc_config_mutable.multiline_output = TRUE; else { - g_string_printf (nmc->return_text, _("Error: '%s' is not a valid argument for '%s' option."), value, argv[0]); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return FALSE; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s' is not a valid argument for '%s' option."), value, argv[0]); + break; } } else if (matches_arg (nmc, &argc, &argv, "-colors", &value)) { if (argc == 1 && nmc->complete) @@ -788,9 +980,10 @@ process_command_line (NmCli *nmc, int argc, char **argv) else if (matches (value, "no")) colors = NMC_USE_COLOR_NO; else { - g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), value, argv[0]); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return FALSE; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s' is not valid argument for '%s' option."), value, argv[0]); + break; } } else if (matches_arg (nmc, &argc, &argv, "-escape", &value)) { if (argc == 1 && nmc->complete) @@ -800,9 +993,10 @@ process_command_line (NmCli *nmc, int argc, char **argv) else if (matches (value, "no")) nmc->nmc_config_mutable.escape_values = FALSE; else { - g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), value, argv[0]); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return FALSE; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s' is not valid argument for '%s' option."), value, argv[0]); + break; } } else if (matches_arg (nmc, &argc, &argv, "-fields", &value)) { if (argc == 1 && nmc->complete) @@ -824,106 +1018,66 @@ process_command_line (NmCli *nmc, int argc, char **argv) unsigned long timeout; if (!nmc_string_to_uint (value, TRUE, 0, G_MAXINT, &timeout)) { - g_string_printf (nmc->return_text, _("Error: '%s' is not a valid timeout."), value); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return FALSE; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: '%s' is not a valid timeout."), value); + break; } nmc->timeout = (int) timeout; } else if (matches_arg (nmc, &argc, &argv, "-version", NULL)) { if (!nmc->complete) g_print (_("nmcli tool, version %s\n"), NMCLI_VERSION); - return NMC_RESULT_SUCCESS; + nmc_run_status_return_success (&nmc->run_status); + break; } else if (matches_arg (nmc, &argc, &argv, "-help", NULL)) { if (!nmc->complete) usage (); - return NMC_RESULT_SUCCESS; + nmc_run_status_return_success (&nmc->run_status); + break; } else { - if (nmc->return_value == NMC_RESULT_SUCCESS) { - g_string_printf (nmc->return_text, _("Error: Option '%s' is unknown, try 'nmcli -help'."), argv[0]); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } - return FALSE; + nmc_run_status_return (&nmc->run_status, + NMC_RESULT_ERROR_USER_INPUT, + _("Error: Option '%s' is unknown, try 'nmcli -help'."), argv[0]); + break; } next_arg (nmc, &argc, &argv, NULL); } - /* Ignore --overview when fields are set explicitly */ - if (nmc->required_fields) - nmc->nmc_config_mutable.overview = FALSE; + if (!nmc_run_status_is_returned (&nmc->run_status)) { + /* Ignore --overview when fields are set explicitly */ + if (nmc->required_fields) + nmc->nmc_config_mutable.overview = FALSE; - set_colors (colors, - &nmc->nmc_config_mutable.use_colors, - &nmc->palette_buffer, - nmc->nmc_config_mutable.palette); + set_colors (colors, + &nmc->nmc_config_mutable.use_colors, + &nmc->palette_buffer, + nmc->nmc_config_mutable.palette); - /* Now run the requested command */ - nmc_do_cmd (nmc, nmcli_cmds, *argv, argc, argv); - - return TRUE; -} - -static gboolean nmcli_sigint = FALSE; - -gboolean -nmc_seen_sigint (void) -{ - return nmcli_sigint; -} - -void -nmc_clear_sigint (void) -{ - nmcli_sigint = FALSE; -} - -void nmc_exit (void) -{ - tcsetattr (STDIN_FILENO, TCSADRAIN, &termios_orig); - nmc_cleanup_readline (); - exit (1); -} - -static gboolean -signal_handler (gpointer user_data) -{ - int signo = GPOINTER_TO_INT (user_data); + /* Now run the requested command */ + nmc_do_cmd (nmc, nmcli_cmds, *argv, argc, argv); + } - switch (signo) { - case SIGINT: - if (nmc_get_in_readline ()) { - nmcli_sigint = TRUE; - } else { - nm_cli.return_value = 0x80 + signo; - g_string_printf (nm_cli.return_text, _("Error: nmcli terminated by signal %s (%d)"), - strsignal (signo), signo); - g_main_loop_quit (loop); + while (TRUE) { + if (nmc_run_status_wait_is_finished (&nmc->run_status)) { + nmc_run_status_return_success_if_necessary (&nmc->run_status); + break; } - break; - case SIGTERM: - nm_cli.return_value = 0x80 + signo; - g_string_printf (nm_cli.return_text, _("Error: nmcli terminated by signal %s (%d)"), - strsignal (signo), signo); - nmc_exit (); - break; + g_main_context_iteration (NULL, TRUE); } - return G_SOURCE_CONTINUE; -} + nm_clear_g_source (& -void -nm_cli_spawn_pager (const NmcConfig *nmc_config, - NmcPagerData *pager_data) -{ - if (pager_data->pid != 0) - return; - pager_data->pid = nmc_terminal_spawn_pager (nmc_config); -} + 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); + } -static void -nmc_cleanup (NmCli *nmc) -{ - pid_t ret; + g_main_loop_unref (loop); g_clear_object (&nmc->client); @@ -952,44 +1106,8 @@ nmc_cleanup (NmCli *nmc) nm_clear_g_free (&nmc->palette_buffer); nmc_polkit_agent_fini (nmc); -} -int -main (int argc, char *argv[]) -{ - /* Set locale to use environment variables */ - setlocale (LC_ALL, ""); - -#ifdef GETTEXT_PACKAGE - /* Set i18n stuff */ - bindtextdomain (GETTEXT_PACKAGE, NMLOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - textdomain (GETTEXT_PACKAGE); -#endif - - /* Save terminal settings */ - tcgetattr (STDIN_FILENO, &termios_orig); - - nm_cli.return_text = g_string_new (_("Success")); - loop = g_main_loop_new (NULL, FALSE); - - g_unix_signal_add (SIGTERM, signal_handler, GINT_TO_POINTER (SIGTERM)); - g_unix_signal_add (SIGINT, signal_handler, GINT_TO_POINTER (SIGINT)); - - if (process_command_line (&nm_cli, argc, argv)) - g_main_loop_run (loop); - - 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); - } - - g_main_loop_unref (loop); - nmc_cleanup (&nm_cli); + g_clear_object (&nmc->run_status.cancellable_mutable); - return nm_cli.return_value; + return nmc->run_status.result_code; } diff --git a/clients/cli/nmcli.h b/clients/cli/nmcli.h index 96a1be1dde..2c185da2b0 100644 --- a/clients/cli/nmcli.h +++ b/clients/cli/nmcli.h @@ -8,6 +8,7 @@ #include "nm-secret-agent-simple.h" #include "nm-meta-setting-desc.h" +#include "c-list/src/c-list.h" struct _NMPolkitListener; @@ -47,9 +48,6 @@ typedef enum { /* Connection/Device/AP not found */ NMC_RESULT_ERROR_NOT_FOUND = 10, - - /* --complete-args signals a file name may follow */ - NMC_RESULT_COMPLETE_FILE = 65, } NMCResultCode; typedef enum { @@ -105,6 +103,27 @@ typedef struct { pid_t pid; } NmcPagerData; +typedef struct { + union { + GCancellable *const cancellable; + GCancellable *cancellable_mutable; + }; + struct { + CList sources_lst_head; + char *result_message; + int should_wait; + NMCResultCode result_code; + bool is_finished:1; + bool is_returned:1; + bool complete_file:1; + } _priv; +} NmcRunStatus; + +typedef struct { + bool in_readline; + bool seen_sigint; +} NmcReadlineStatus; + typedef struct _NmcOutputData { GPtrArray *output_data; /* GPtrArray of arrays of NmcOutputField structs - accumulates data for output */ } NmcOutputData; @@ -113,9 +132,8 @@ typedef struct _NmcOutputData { typedef struct _NmCli { NMClient *client; /* Pointer to NMClient of libnm */ - NMCResultCode return_value; /* Return code of nmcli */ - GString *return_text; /* Reason text */ - + NmcRunStatus run_status; + NmcReadlineStatus readline_status; NmcPagerData pager_data; int timeout; /* Operation timeout */ @@ -124,7 +142,6 @@ typedef struct _NmCli { GHashTable *pwds_hash; /* Hash table with passwords in passwd-file */ struct _NMPolkitListener *pk_listener; /* polkit agent listener */ - int should_wait; /* Semaphore indicating whether nmcli should not end or not yet */ gboolean nowait_flag; /* '--nowait' option; used for passing to callbacks */ gboolean mode_specified; /* Whether tabular/multiline mode was specified via '--mode' option */ union { @@ -133,7 +150,12 @@ typedef struct _NmCli { }; char *required_fields; /* Required fields in output: '--fields' option */ gboolean ask; /* Ask for missing parameters: option '--ask' */ - gboolean complete; /* Autocomplete the command line */ + + union { + const bool complete; + bool complete_mutable; + }; + gboolean editor_status_line; /* Whether to display status line in connection editor */ gboolean editor_save_confirmation; /* Whether to ask for confirmation on saving connections with 'autoconnect=yes' */ @@ -148,8 +170,8 @@ GQuark nmcli_error_quark (void); extern GMainLoop *loop; -gboolean nmc_seen_sigint (void); -void nmc_clear_sigint (void); +gboolean nmc_seen_sigint (NmcReadlineStatus *readline_status); +void nmc_clear_sigint (NmcReadlineStatus *readline_status); void nmc_set_sigquit_internal (void); void nmc_exit (void); @@ -164,4 +186,157 @@ void nmc_empty_output_fields (NmcOutputData *output_data); .output_data = g_ptr_array_new_full (20, g_free), \ } +/*****************************************************************************/ + +void _nmc_run_status_signal_finished (NmcRunStatus *run_status); + +static inline void +nmc_run_status_wait_push (NmcRunStatus *run_status) +{ + nm_assert (run_status); + nm_assert (run_status->_priv.should_wait < G_MAXINT); + nm_assert (!run_status->_priv.is_finished); + + run_status->_priv.should_wait++; +} + +static inline void +nmc_run_status_wait_pop (NmcRunStatus *run_status) +{ + nm_assert (run_status); + nm_assert (run_status->_priv.should_wait > 0); + nm_assert (!run_status->_priv.is_finished); + + if (--run_status->_priv.should_wait == 0) + _nmc_run_status_signal_finished (run_status); +} + +static inline void +_nm_auto_pop_run_status (NmCli **p_cli) +{ + if (*p_cli) + nmc_run_status_wait_pop (&((*p_cli)->run_status)); +} + +#define nm_auto_pop_run_status nm_auto (_nm_auto_pop_run_status) + +static inline gboolean +nmc_run_status_wait_is_finished (NmcRunStatus *run_status) +{ + nm_assert (run_status); + nm_assert (run_status->_priv.should_wait >= 0); + nm_assert (!run_status->_priv.is_finished); + + if (run_status->_priv.should_wait > 0) + return FALSE; + _nmc_run_status_signal_finished (run_status); + return TRUE; +} + +GSource *nmc_run_status_wait_add_timeout_full (NmcRunStatus *run_status, + guint timeout_msec, + GSourceFunc func, + gpointer user_data, + GDestroyNotify destroy_notify); +static inline GSource * +nmc_run_status_wait_add_timeout (NmcRunStatus *run_status, + guint timeout_msec, + GSourceFunc func, + gpointer user_data) +{ + return nmc_run_status_wait_add_timeout_full (run_status, timeout_msec, func, user_data, NULL); +} + +void nmc_run_status_wait_remove_source (NmcRunStatus *run_status, + GSource *source_take); + +static inline gboolean +nmc_run_status_is_returned (NmcRunStatus *run_status) +{ + return run_status->_priv.is_returned; +} + +void nmc_run_status_return_message_full (NmcRunStatus *run_status, + NMCResultCode result_code, + char *result_message_take); + +static inline void +nmc_run_status_return_message (NmcRunStatus *run_status, + NMCResultCode result_code, + const char *result_message) +{ + nmc_run_status_return_message_full (run_status, result_code, g_strdup (result_message)); +} + +#define nmc_run_status_return(run_status, result_code, ...) \ + G_STMT_START { \ + NmcRunStatus *_run_status = (run_status); \ + NMCResultCode _result_code = (result_code); \ + \ + if (NM_NARG (__VA_ARGS__) == 1) \ + nmc_run_status_return_message (_run_status, _result_code, _NM_UTILS_MACRO_FIRST (__VA_ARGS__)); \ + else { \ + gs_free char *_msg = g_strdup_printf (__VA_ARGS__); \ + \ + nmc_run_status_return_message (_run_status, _result_code, _msg); \ + } \ + } G_STMT_END + +static inline void +nmc_run_status_return_success (NmcRunStatus *run_status) +{ + nmc_run_status_return_message (run_status, NMC_RESULT_SUCCESS, NULL); +} + +static inline void +nmc_run_status_return_success_if_necessary (NmcRunStatus *run_status) +{ + /* By default, if the result was not set upon completion we anyway assume + * success. So, this has only the purpose to mark the result as set so + * that we assert that we won't try to set the result again. This is + * only to catch bugs where we try to set the result multiple times. */ + nm_assert (run_status); + if (!run_status->_priv.is_returned) + nmc_run_status_return_success (run_status); +} + +void nmc_run_status_return_append_message (NmcRunStatus *run_status, + const char *fmt, + ...) G_GNUC_PRINTF (2, 3); + +static inline void +nmc_run_status_set_complete_file (NmcRunStatus *run_status) +{ + nm_assert (run_status); + nm_assert (!run_status->_priv.is_returned); + + run_status->_priv.complete_file = TRUE; +} + +/*****************************************************************************/ + +struct _NMCCommand; + +typedef struct _NMCCommand { + const char *cmd; + void (*func) (const struct _NMCCommand *cmd, + NmCli *nmc, + int argc, + const char *const*argv); + void (*usage) (void); + bool needs_client; + bool needs_nm_running; +} NMCCommand; + +void nmc_command_do_agent (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv); +void nmc_command_do_general (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv); +void nmc_command_do_networking (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv); +void nmc_command_do_radio (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv); +void nmc_command_do_monitor (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv); +void nmc_command_do_overview (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv); +void nmc_command_do_connection (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv); +void nmc_command_do_device (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv); + +/*****************************************************************************/ + #endif /* NMC_NMCLI_H */ diff --git a/clients/cli/utils.c b/clients/cli/utils.c index 67d5a742a0..61aa0a629a 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -154,7 +154,7 @@ parse_global_arg (NmCli *nmc, const char *arg) * -1 otherwise (no more args). */ int -next_arg (NmCli *nmc, int *argc, char ***argv, ...) +next_arg (NmCli *nmc, int *argc, const char *const**argv, ...) { va_list args; const char *cmd_option; @@ -164,20 +164,20 @@ next_arg (NmCli *nmc, int *argc, char ***argv, ...) do { int cmd_option_pos = 1; - if (*argc > 0) { - (*argc)--; - (*argv)++; - } + if (*argc > 0) + arg_move_next (argc, argv); if (*argc == 0) return -1; va_start (args, argv); - if (nmc && nmc->complete && *argc == 1) { + if ( nmc + && nmc->complete + && *argc == 1) { while ((cmd_option = va_arg (args, const char *))) nmc_complete_strings (**argv, cmd_option); - if (***argv == '-') + if ((**argv)[0] == '-') nmc_complete_strings (**argv, "--ask", "--show-secrets"); va_end (args); @@ -248,7 +248,11 @@ nmc_arg_is_option (const char *str, const char *opt_name) * Returns: TRUE on success, FALSE on an error and sets 'error' */ gboolean -nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GError **error) +nmc_parse_args (nmc_arg_t *arg_arr, + gboolean last, + int *argc, + const char *const**argv, + GError **error) { nmc_arg_t *p; gboolean found; @@ -483,8 +487,11 @@ nmc_get_user_input (const char *ask_str) * Split string in 'line' according to 'delim' to (argument) array. */ int -nmc_string_to_arg_array (const char *line, const char *delim, gboolean unquote, - char ***argv, int *argc) +nmc_string_to_arg_array (const char *line, + const char *delim, + gboolean unquote, + char ***argv, + int *argc) { gs_free const char **arr0 = NULL; char **arr; diff --git a/clients/cli/utils.h b/clients/cli/utils.h index 87e93ce082..1cdde93c8e 100644 --- a/clients/cli/utils.h +++ b/clients/cli/utils.h @@ -8,8 +8,6 @@ #include "nmcli.h" -/* === Types === */ - typedef struct { const char *name; gboolean has_value; @@ -18,11 +16,26 @@ typedef struct { gboolean found; } nmc_arg_t; -/* === Functions === */ -int next_arg (NmCli *nmc, int *argc, char ***argv, ...); +static inline void +arg_move_next (int *argc, const char *const**argv) +{ + nm_assert (argc); + nm_assert (argv); + nm_assert (*argc > 0); + nm_assert ((gsize) *argc == NM_PTRARRAY_LEN (argv)); + + argc--; + argv++; +} + +int next_arg (NmCli *nmc, int *argc, const char *const**argv, ...) G_GNUC_NULL_TERMINATED; gboolean nmc_arg_is_help (const char *arg); gboolean nmc_arg_is_option (const char *arg, const char *opt_name); -gboolean nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GError **error); +gboolean nmc_parse_args (nmc_arg_t *arg_arr, + gboolean last, + int *argc, + const char *const**argv, + GError **error); char *ssid_to_hex (const char *str, gsize len); void nmc_terminal_erase_line (void); void nmc_terminal_show_progress (const char *str); @@ -31,8 +44,11 @@ char *nmc_colorize (const NmcConfig *nmc_config, NMMetaColor color, const char * 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); -int nmc_string_to_arg_array (const char *line, const char *delim, gboolean unquote, - char ***argv, int *argc); +int nmc_string_to_arg_array (const char *line, + const char *delim, + gboolean unquote, + char ***argv, + int *argc); const char *nmc_string_is_valid (const char *input, const char **allowed, GError **error); char * nmc_util_strv_for_display (const char *const*strv, gboolean brackets); int nmc_string_screen_width (const char *start, const char *end); |