mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-05 19:18:00 +02:00
Merge changes for Ctrl-C and Ctrl-D handling in nmcli/readline (bgo #706118)
https://bugzilla.gnome.org/show_bug.cgi?id=706118
This commit is contained in:
commit
c8902e8171
5 changed files with 150 additions and 10 deletions
|
|
@ -1101,6 +1101,32 @@ nmc_cleanup_readline (void)
|
|||
rl_cleanup_after_signal ();
|
||||
}
|
||||
|
||||
|
||||
static gboolean nmcli_in_readline = FALSE;
|
||||
static pthread_mutex_t readline_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
gboolean
|
||||
nmc_get_in_readline (void)
|
||||
{
|
||||
gboolean in_readline;
|
||||
|
||||
pthread_mutex_lock (&readline_mutex);
|
||||
in_readline = nmcli_in_readline;
|
||||
pthread_mutex_unlock (&readline_mutex);
|
||||
return in_readline;
|
||||
}
|
||||
|
||||
void
|
||||
nmc_set_in_readline (gboolean in_readline)
|
||||
{
|
||||
pthread_mutex_lock (&readline_mutex);
|
||||
nmcli_in_readline = in_readline;
|
||||
pthread_mutex_unlock (&readline_mutex);
|
||||
}
|
||||
|
||||
/* Global variable defined in nmcli.c */
|
||||
extern NmCli nm_cli;
|
||||
|
||||
/**
|
||||
* nmc_readline:
|
||||
* @prompt_fmt: prompt to print (telling user what to enter). It is standard
|
||||
|
|
@ -1108,6 +1134,9 @@ nmc_cleanup_readline (void)
|
|||
* @...: a list of arguments according to the @prompt_fmt format string
|
||||
*
|
||||
* Wrapper around libreadline's readline() function.
|
||||
* If user pressed Ctrl-C, readline() is called again (if not in editor and
|
||||
* line is empty, nmcli will quit).
|
||||
* If user pressed Ctrl-D on empty line, nmcli will quit.
|
||||
*
|
||||
* Returns: the user provided string. In case the user entered empty string,
|
||||
* this function returns NULL.
|
||||
|
|
@ -1122,17 +1151,52 @@ nmc_readline (const char *prompt_fmt, ...)
|
|||
prompt = g_strdup_vprintf (prompt_fmt, args);
|
||||
va_end (args);
|
||||
|
||||
readline_mark:
|
||||
/* We are in readline -> Ctrl-C should not quit nmcli */
|
||||
nmc_set_in_readline (TRUE);
|
||||
str = readline (prompt);
|
||||
/* We are outside readline -> Ctrl-C should quit nmcli */
|
||||
nmc_set_in_readline (FALSE);
|
||||
|
||||
/* Add string to the history */
|
||||
if (str && *str)
|
||||
add_history (str);
|
||||
|
||||
/*-- React on Ctrl-C and Ctrl-D --*/
|
||||
/* We quit on Ctrl-D when line is empty */
|
||||
if (str == NULL) {
|
||||
/* Send SIGQUIT to itself */
|
||||
nmc_set_sigquit_internal ();
|
||||
kill (getpid (), SIGQUIT);
|
||||
/* Sleep in this thread so that we don't do anything else until exit */
|
||||
for (;;)
|
||||
sleep (3);
|
||||
}
|
||||
/* Ctrl-C */
|
||||
if (nmc_seen_sigint ()) {
|
||||
nmc_clear_sigint ();
|
||||
if (nm_cli.in_editor || *str) {
|
||||
/* In editor, or the line is not empty */
|
||||
/* Call readline again to get new prompt (repeat) */
|
||||
g_free (str);
|
||||
goto readline_mark;
|
||||
} else {
|
||||
/* Not in editor and line is empty */
|
||||
/* Send SIGQUIT to itself */
|
||||
nmc_set_sigquit_internal ();
|
||||
kill (getpid (), SIGQUIT);
|
||||
/* Sleep in this thread so that we don't do anything else until exit */
|
||||
for (;;)
|
||||
sleep (3);
|
||||
}
|
||||
}
|
||||
g_free (prompt);
|
||||
|
||||
/* Return NULL, not empty string */
|
||||
if (str && *str == '\0') {
|
||||
g_free (str);
|
||||
str = NULL;
|
||||
}
|
||||
|
||||
if (str && *str)
|
||||
add_history (str);
|
||||
|
||||
g_free (prompt);
|
||||
return str;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,5 +62,7 @@ NMConnection *nmc_find_connection (GSList *list,
|
|||
void nmc_cleanup_readline (void);
|
||||
char *nmc_readline (const char *prompt_fmt, ...) G_GNUC_PRINTF (1, 2);
|
||||
char *nmc_rl_gen_func_basic (const char *text, int state, const char **words);
|
||||
gboolean nmc_get_in_readline (void);
|
||||
void nmc_set_in_readline (gboolean in_readline);
|
||||
|
||||
#endif /* NMC_COMMON_H */
|
||||
|
|
|
|||
|
|
@ -7982,6 +7982,9 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
|
|||
editor_init_new_connection (nmc, connection);
|
||||
}
|
||||
|
||||
/* nmcli runs the editor */
|
||||
nmc->in_editor = TRUE;
|
||||
|
||||
printf ("\n");
|
||||
printf (_("===| nmcli interactive connection editor |==="));
|
||||
printf ("\n\n");
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <locale.h>
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
|
@ -270,6 +272,46 @@ parse_command_line (NmCli *nmc, int argc, char **argv)
|
|||
return nmc->return_value;
|
||||
}
|
||||
|
||||
static gboolean nmcli_sigint = FALSE;
|
||||
static pthread_mutex_t sigint_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static gboolean nmcli_sigquit_internal = FALSE;
|
||||
|
||||
gboolean
|
||||
nmc_seen_sigint (void)
|
||||
{
|
||||
gboolean sigint;
|
||||
|
||||
pthread_mutex_lock (&sigint_mutex);
|
||||
sigint = nmcli_sigint;
|
||||
pthread_mutex_unlock (&sigint_mutex);
|
||||
return sigint;
|
||||
}
|
||||
|
||||
void
|
||||
nmc_clear_sigint (void)
|
||||
{
|
||||
pthread_mutex_lock (&sigint_mutex);
|
||||
nmcli_sigint = FALSE;
|
||||
pthread_mutex_unlock (&sigint_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
nmc_set_sigquit_internal (void)
|
||||
{
|
||||
nmcli_sigquit_internal = TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
event_hook_for_readline (void)
|
||||
{
|
||||
/* Make readline() exit on SIGINT */
|
||||
if (nmc_seen_sigint ()) {
|
||||
rl_echo_signal_char (SIGINT);
|
||||
rl_stuff_char ('\n');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *signal_handling_thread (void *arg);
|
||||
/*
|
||||
* Thread function waiting for signals and processing them.
|
||||
|
|
@ -287,10 +329,25 @@ signal_handling_thread (void *arg) {
|
|||
|
||||
switch (signo) {
|
||||
case SIGINT:
|
||||
if (nmc_get_in_readline ()) {
|
||||
/* Don't quit when in readline, only signal we received SIGINT */
|
||||
pthread_mutex_lock (&sigint_mutex);
|
||||
nmcli_sigint = TRUE;
|
||||
pthread_mutex_unlock (&sigint_mutex);
|
||||
} else {
|
||||
/* We can quit nmcli */
|
||||
nmc_cleanup_readline ();
|
||||
printf (_("\nError: nmcli terminated by signal %s (%d)\n"),
|
||||
strsignal (signo), signo);
|
||||
exit (1);
|
||||
}
|
||||
break;
|
||||
case SIGQUIT:
|
||||
case SIGTERM:
|
||||
nmc_cleanup_readline ();
|
||||
printf (_("\nError: nmcli terminated by signal %d."), signo);
|
||||
if (!nmcli_sigquit_internal)
|
||||
printf (_("\nError: nmcli terminated by signal %s (%d)\n"),
|
||||
strsignal (signo), signo);
|
||||
exit (1);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -320,14 +377,14 @@ setup_signals (void)
|
|||
/* Block all signals of interest. */
|
||||
status = pthread_sigmask (SIG_BLOCK, &signal_set, NULL);
|
||||
if (status != 0) {
|
||||
fprintf (stderr, _("Failed to set signal mask: %d"), status);
|
||||
fprintf (stderr, _("Failed to set signal mask: %d\n"), status);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Create the signal handling thread. */
|
||||
/* Create the signal handling thread. */
|
||||
status = pthread_create (&signal_thread_id, NULL, signal_handling_thread, NULL);
|
||||
if (status != 0) {
|
||||
fprintf (stderr, _("Failed to create signal handling thread: %d"), status);
|
||||
fprintf (stderr, _("Failed to create signal handling thread: %d\n"), status);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
@ -375,6 +432,7 @@ nmc_init (NmCli *nmc)
|
|||
memset (&nmc->print_fields, '\0', sizeof (NmcPrintFields));
|
||||
nmc->nocheck_ver = FALSE;
|
||||
nmc->ask = FALSE;
|
||||
nmc->in_editor = FALSE;
|
||||
nmc->editor_status_line = FALSE;
|
||||
nmc->editor_save_confirmation = TRUE;
|
||||
nmc->editor_prompt_color = NMC_TERM_COLOR_NORMAL;
|
||||
|
|
@ -431,6 +489,13 @@ main (int argc, char *argv[])
|
|||
g_type_init ();
|
||||
#endif
|
||||
|
||||
/* readline init */
|
||||
rl_event_hook = event_hook_for_readline;
|
||||
/* Set 0.01s timeout to mitigate slowness in readline when a broken version is used.
|
||||
* See https://bugzilla.redhat.com/show_bug.cgi?id=1109946
|
||||
*/
|
||||
rl_set_keyboard_input_timeout (10000);
|
||||
|
||||
nmc_init (&nm_cli);
|
||||
g_idle_add (start, &args_info);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* (C) Copyright 2010 - 2012 Red Hat, Inc.
|
||||
* (C) Copyright 2010 - 2014 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#ifndef NMC_NMCLI_H
|
||||
|
|
@ -128,6 +128,7 @@ typedef struct _NmCli {
|
|||
NmcPrintFields print_fields; /* Structure with field indices to print */
|
||||
gboolean nocheck_ver; /* Don't check nmcli and NM versions: option '--nocheck' */
|
||||
gboolean ask; /* Ask for missing parameters: option '--ask' */
|
||||
gboolean in_editor; /* Whether running the editor - nmcli con edit' */
|
||||
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' */
|
||||
NmcTermColor editor_prompt_color; /* Color of prompt in connection editor */
|
||||
|
|
@ -137,4 +138,9 @@ typedef struct _NmCli {
|
|||
#define NMCLI_ERROR (nmcli_error_quark ())
|
||||
GQuark nmcli_error_quark (void);
|
||||
|
||||
gboolean nmc_seen_sigint (void);
|
||||
void nmc_clear_sigint (void);
|
||||
void nmc_set_sigquit_internal (void);
|
||||
|
||||
|
||||
#endif /* NMC_NMCLI_H */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue