diff options
-rw-r--r-- | clients/cli/Makefile.am | 2 | ||||
-rw-r--r-- | clients/cli/agent.c | 251 | ||||
-rw-r--r-- | clients/cli/agent.h | 29 | ||||
-rw-r--r-- | clients/cli/connections.c | 4 | ||||
-rw-r--r-- | clients/cli/devices.c | 4 | ||||
-rw-r--r-- | clients/cli/general.c | 10 | ||||
-rw-r--r-- | clients/cli/nmcli-completion | 7 | ||||
-rw-r--r-- | clients/cli/nmcli.c | 11 | ||||
-rw-r--r-- | clients/cli/polkit-agent.c | 28 | ||||
-rw-r--r-- | clients/cli/polkit-agent.h | 2 | ||||
-rw-r--r-- | man/nmcli.1.in | 37 | ||||
-rw-r--r-- | po/POTFILES.in | 1 |
12 files changed, 371 insertions, 15 deletions
diff --git a/clients/cli/Makefile.am b/clients/cli/Makefile.am index 2060c4bfff..0820aab045 100644 --- a/clients/cli/Makefile.am +++ b/clients/cli/Makefile.am @@ -16,6 +16,8 @@ AM_CPPFLAGS = \ -DNMCLI_LOCALEDIR=\"$(datadir)/locale\" nmcli_SOURCES = \ + agent.c \ + agent.h \ common.c \ common.h \ connections.c \ diff --git a/clients/cli/agent.c b/clients/cli/agent.c new file mode 100644 index 0000000000..9d4869b11e --- /dev/null +++ b/clients/cli/agent.c @@ -0,0 +1,251 @@ +/* + * nmcli - command-line tool for controlling NetworkManager + * Functions for running NM secret agent. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2014 Red Hat, Inc. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <readline/readline.h> +#include <readline/history.h> + +#include "common.h" +#include "utils.h" +#include "nm-secret-agent-simple.h" +#include "polkit-agent.h" +#include "agent.h" + +static void +usage (void) +{ + g_printerr (_("Usage: nmcli agent { COMMAND | help }\n\n" + "COMMAND := { secret | polkit | all }\n\n" + )); +} + +static void +usage_agent_secret (void) +{ + g_printerr (_("Usage: nmcli agent secret { help }\n" + "\n" + "Runs nmcli as NetworkManager secret agent. When NetworkManager requires\n" + "a password it asks registered agents for it. This command keeps nmcli running\n" + "and if a password is required asks the user for it.\n\n")); +} + +static void +usage_agent_polkit (void) +{ + g_printerr (_("Usage: nmcli agent polkit { help }\n" + "\n" + "Registers nmcli as a polkit action for the user session.\n" + "When a polkit daemon requires an authorization, nmcli asks the user and gives\n" + "the reponse back to polkit.\n\n")); +} + +static void +usage_agent_all (void) +{ + g_printerr (_("Usage: nmcli agent all { help }\n" + "\n" + "Runs nmcli as both NetworkManager secret and a polkit agent.\n\n")); +} + +/* for pre-filling a string to readline prompt */ +static char *pre_input_deftext; +static int +set_deftext (void) +{ + if (pre_input_deftext && rl_startup_hook) { + rl_insert_text (pre_input_deftext); + g_free (pre_input_deftext); + pre_input_deftext = NULL; + rl_startup_hook = NULL; + } + return 0; +} + +static gboolean +get_secrets_from_user (const char *request_id, + const char *title, + const char *msg, + GPtrArray *secrets) +{ + int i; + + for (i = 0; i < secrets->len; i++) { + NMSecretAgentSimpleSecret *secret = secrets->pdata[i]; + char *pwd = NULL; + + /* Ask user for the password */ + g_print ("%s\n", msg); + if (secret->value) { + /* Prefill the password if we have it. */ + rl_startup_hook = set_deftext; + pre_input_deftext = g_strdup (secret->value); + } + pwd = nmc_readline ("%s (%s): ", secret->name, secret->prop_name); + + /* No password provided, cancel the secrets. */ + if (!pwd) + return FALSE; + g_free (secret->value); + secret->value = pwd; + } + return TRUE; +} + +static void +secrets_requested (NMSecretAgentSimple *agent, + const char *request_id, + const char *title, + const char *msg, + GPtrArray *secrets, + gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + gboolean success = FALSE; + + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + + success = get_secrets_from_user (request_id, title, msg, secrets); + if (success) + nm_secret_agent_simple_response (agent, request_id, secrets); + else + nm_secret_agent_simple_response (agent, request_id, NULL); +} + + +static NMCResultCode +do_agent_secret (NmCli *nmc, int argc, char **argv) +{ + /* Create secret agent */ + nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-agent"); + if (nmc->secret_agent) { + /* We keep running */ + nmc->should_wait = TRUE; + + g_signal_connect (nmc->secret_agent, "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 NMCResultCode +do_agent_polkit (NmCli *nmc, int argc, char **argv) +{ + GError *error = NULL; + + /* Initialize polkit agent */ + 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; + g_error_free (error); + } else { + /* We keep running */ + nmc->should_wait = TRUE; + + g_print (_("nmcli successfully registered as a polkit agent.\n")); + } + + return nmc->return_value; +} + +static NMCResultCode +do_agent_all (NmCli *nmc, int argc, char **argv) +{ + NMCResultCode secret_res; + + /* 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); + + nmc->return_value = do_agent_polkit (nmc, argc, argv); + + if (nmc->return_value == NMC_RESULT_SUCCESS && secret_res != NMC_RESULT_SUCCESS) + nmc->return_value = secret_res; + + return nmc->return_value; +} + +NMCResultCode +do_agent (NmCli *nmc, int argc, char **argv) +{ + /* Get NMClient object */ + nmc->get_client (nmc); + + /* Check whether NetworkManager is running */ + if (!nm_client_get_nm_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + return nmc->return_value; + } + /* Compare NM and nmcli versions */ + if (!nmc_versions_match (nmc)) + return nmc->return_value; + + if (argc == 0) { + nmc->return_value = do_agent_all (nmc, 0, NULL); + } + + if (argc > 0) { + if (nmc_arg_is_help (*argv)) { + usage (); + goto usage_exit; + } else if (matches (*argv, "secret") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_agent_secret (); + goto usage_exit; + } + nmc->return_value = do_agent_secret (nmc, argc-1, argv+1); + } else if (matches (*argv, "polkit") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_agent_polkit (); + goto usage_exit; + } + nmc->return_value = do_agent_polkit (nmc, argc-1, argv+1); + } else if (matches (*argv, "all") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_agent_all (); + goto usage_exit; + } + nmc->return_value = do_agent_all (nmc, argc-1, argv+1); + } else { + usage (); + g_string_printf (nmc->return_text, _("Error: 'agent' command '%s' is not valid."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } + +usage_exit: + return nmc->return_value; +} diff --git a/clients/cli/agent.h b/clients/cli/agent.h new file mode 100644 index 0000000000..70ea0d9b23 --- /dev/null +++ b/clients/cli/agent.h @@ -0,0 +1,29 @@ +/* + * nmcli - command-line tool for controlling NetworkManager + * Functions for running NM secret agent. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 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/connections.c b/clients/cli/connections.c index 717945b33d..554e2107e6 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -36,6 +36,7 @@ #include "settings.h" #include "connections.h" #include "nm-secret-agent-simple.h" +#include "polkit-agent.h" /* define some prompts for connection editor */ #define EDITOR_PROMPT_SETTING _("Setting name? ") @@ -8796,6 +8797,9 @@ do_connections (NmCli *nmc, int argc, char **argv) { GError *error = NULL; + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + /* Set completion function for 'nmcli con' */ rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_tab_completion; diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 3d9598fb41..c073ec252b 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -28,6 +28,7 @@ #include <glib.h> #include <glib/gi18n.h> +#include "polkit-agent.h" #include "utils.h" #include "common.h" #include "devices.h" @@ -2776,6 +2777,9 @@ do_devices (NmCli *nmc, int argc, char **argv) { GError *error = NULL; + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + rl_attempted_completion_function = (rl_completion_func_t *) nmcli_device_tab_completion; /* Get NMClient object early */ diff --git a/clients/cli/general.c b/clients/cli/general.c index 531d4f7acd..44c63c356c 100644 --- a/clients/cli/general.c +++ b/clients/cli/general.c @@ -25,6 +25,7 @@ #include <glib.h> #include <glib/gi18n.h> +#include "polkit-agent.h" #include "utils.h" #include "general.h" @@ -558,6 +559,9 @@ do_general (NmCli *nmc, int argc, char **argv) { GError *error = NULL; + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + if (argc == 0) { if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { g_string_printf (nmc->return_text, _("Error: %s."), error->message); @@ -726,6 +730,9 @@ do_networking (NmCli *nmc, int argc, char **argv) { gboolean enable_flag; + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + if (argc == 0) nmc_switch_show (nmc, NMC_FIELDS_NM_NETWORKING, _("Networking")); else if (argc > 0) { @@ -787,6 +794,9 @@ do_radio (NmCli *nmc, int argc, char **argv) GError *error = NULL; gboolean enable_flag; + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + if (argc == 0) { if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { g_string_printf (nmc->return_text, _("Error: %s."), error->message); diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index 1e087f7419..72648d43cb 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -701,7 +701,7 @@ _nmcli() # (if the current word starts with a dash) or the OBJECT list # otherwise. if [[ "${words[0]:0:1}" != '-' ]]; then - OPTIONS=(help general networking radio connection device) + OPTIONS=(help general networking radio connection device agent) elif [[ "${words[0]:1:1}" == '-' || "${words[0]}" == "-" ]]; then OPTIONS=("${LONG_OPTIONS[@]/#/--}") else @@ -1268,6 +1268,11 @@ _nmcli() esac fi ;; + a|ag|age|agen|agent) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" secret polkit all + fi + ;; esac return 0 diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c index 76033417ee..ade0a6bd4d 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -43,6 +43,7 @@ #include "connections.h" #include "devices.h" #include "general.h" +#include "agent.h" #if defined(NM_DIST_VERSION) # define NMCLI_VERSION NM_DIST_VERSION @@ -102,6 +103,7 @@ usage (const char *prog_name) " r[adio] NetworkManager radio switches\n" " c[onnection] NetworkManager's connections\n" " d[evice] devices managed by NetworkManager\n" + " a[gent] NetworkManager secret agent or polkit agent\n" "\n"), prog_name); } @@ -122,6 +124,7 @@ static const struct cmd { { "radio", do_radio }, { "connection", do_connections }, { "device", do_devices }, + { "agent", do_agent }, { "help", do_help }, { 0 } }; @@ -266,14 +269,6 @@ parse_command_line (NmCli *nmc, int argc, char **argv) } if (argc > 1) { - GError *error = NULL; - - /* Initialize polkit agent */ - if (!nmc_polkit_agent_init (&nm_cli, FALSE, &error)) { - g_printerr ("Polkit agent initialization failed: %s\n", error->message); - g_error_free (error); - } - /* Now run the requested command */ return do_cmd (nmc, argv[1], argc-1, argv+1); } diff --git a/clients/cli/polkit-agent.c b/clients/cli/polkit-agent.c index e8502884e4..a163b824fa 100644 --- a/clients/cli/polkit-agent.c +++ b/clients/cli/polkit-agent.c @@ -102,10 +102,6 @@ nmc_polkit_agent_init (NmCli* nmc, gboolean for_session, GError **error) g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - /* We don't register polkit agent at all when running non-interactively */ - if (!nmc->ask) - return TRUE; - listener = nm_polkit_listener_new (for_session, error); if (!listener) return FALSE; @@ -125,6 +121,24 @@ nmc_polkit_agent_fini (NmCli* nmc) g_clear_object (&nmc->pk_listener); } +gboolean +nmc_start_polkit_agent_start_try (NmCli *nmc) +{ + GError *error = NULL; + + /* We don't register polkit agent at all when running non-interactively */ + if (!nmc->ask) + return TRUE; + + if (!nmc_polkit_agent_init (nmc, FALSE, &error)) { + g_printerr (_("Warning: polkit agent initialization failed: %s\n"), + error->message); + g_error_free (error); + return FALSE; + } + return TRUE; +} + #else /* polkit agent is not avalable; implement stub functions. */ @@ -143,4 +157,10 @@ nmc_polkit_agent_fini (NmCli* nmc) { } +gboolean +nmc_start_polkit_agent_start_try (NmCli *nmc) +{ + return TRUE; +} + #endif /* #if WITH_POLKIT_AGENT */ diff --git a/clients/cli/polkit-agent.h b/clients/cli/polkit-agent.h index 2e0326bc97..e2902dc0b1 100644 --- a/clients/cli/polkit-agent.h +++ b/clients/cli/polkit-agent.h @@ -25,4 +25,6 @@ gboolean nmc_polkit_agent_init (NmCli *nmc, gboolean for_session, GError **error); void nmc_polkit_agent_fini (NmCli* nmc); +gboolean nmc_start_polkit_agent_start_try (NmCli *nmc); + #endif /* __NMC_POLKIT_AGENT_H__ */ diff --git a/man/nmcli.1.in b/man/nmcli.1.in index 2d142f4eaa..66e1567d45 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -21,7 +21,7 @@ .\" .\" Copyright 2010 - 2014 Red Hat, Inc. .\" -.TH NMCLI "1" "11 September 2014" +.TH NMCLI "1" "4 November 2014" .SH NAME nmcli \- command\(hyline tool for controlling NetworkManager @@ -33,7 +33,7 @@ nmcli \- command\(hyline tool for controlling NetworkManager .sp .IR OBJECT " := { " -.BR general " | " networking " | " radio " | " connection " | " device +.BR general " | " networking " | " radio " | " connection " | " device " | " agent .RI " }" .sp @@ -793,6 +793,39 @@ This command does not show the APs, use 'nmcli device wifi list' for that. List available WiMAX NSP. The \fIifname\fP and \fInsp\fP options can be used to list networks for a particular interface or with a specific NSP, respectively. +.RE + +.TP +.B agent \- run nmcli as a NetworkManager secret agent, or polkit agent +.br +.TP +.SS \fICOMMAND\fP := { secret | polkit | all } +.sp +.RS +.TP +.B secret +.br +Register nmcli as a NetworkManager secret agent and listen for secret requests. +You do usually not need this command, because nmcli can handle secrets when +connecting to networks. However, you may find the command useful when you use +another tool for activating connections and you do not have a secret agent +available (like nm-applet). +.TP +.B polkit +.br +Register nmcli as a polkit agent for the user session and listen for +authorization requests. You do not usually need this command, because nmcli can +handle polkit actions related to NetworkManager operations (when run with +--ask). However, you may find the command useful when you want to run a simple +text based polkit agent and you do not have an agent of a desktop environment. +Note that running this command makes nmcli handle all polkit requests, not only +NetworkManager related ones, because only one polkit agent can run for the +session. +.TP +.B all +.br +Runs nmcli as both NetworkManager secret and a polkit agent. +.RE .SH ENVIRONMENT VARIABLES \fInmcli\fP's behavior is affected by the following environment variables. diff --git a/po/POTFILES.in b/po/POTFILES.in index 5417b07dc3..6d23a67b77 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,6 +1,7 @@ [encoding: UTF-8] # List of source files containing translatable strings. # Please keep this file sorted alphabetically. +clients/cli/agent.c clients/cli/common.c clients/cli/connections.c clients/cli/devices.c |