nmcli: fix endless loop with --offline --ask

If --offline and --ask were used at the same time, and endless loop
showing the readline's prompt but without waiting for user's input
happened.

This was because when using --offline, all arguments are parsed and
resolved before running the g_main_loop. In nmc_readline_helper it was
checked that the main loop is running, so if g_main_loop_quit is called
we can stop waiting for user's input.

Fix this bug by continue polling for user input if the main loop is
running or if we are in offline mode.  Cancelling the user input is
still possible both in normal and offline mode with Ctrl+C or Ctrl+D.

Added a test case to verify that this still works after future changes.
This commit is contained in:
Íñigo Huguet 2023-07-17 10:56:35 +02:00
parent 5490604084
commit d414265ab1
2 changed files with 57 additions and 4 deletions

View file

@ -880,7 +880,8 @@ read_again:
rl_got_line = FALSE;
rl_callback_handler_install(prompt, readline_cb);
while (!rl_got_line && g_main_loop_is_running(loop) && !nmc_seen_sigint())
while (!rl_got_line && (g_main_loop_is_running(loop) || nmc_config->offline)
&& !nmc_seen_sigint())
g_main_context_iteration(NULL, TRUE);
/* If Ctrl-C was detected, complete the line */
@ -909,7 +910,7 @@ read_again:
}
} else if (!rl_string) {
/* Ctrl-D, exit */
if (g_main_loop_is_running(loop))
if (g_main_loop_is_running(loop) || nmc_config->offline)
nmc_exit();
}

View file

@ -703,11 +703,14 @@ class Util:
return typ(pexp, valgrind_log)
@staticmethod
def cmd_call_pexpect_nmcli(args):
def cmd_call_pexpect_nmcli(args, extra_env={}):
extra_env = extra_env.copy()
extra_env.update({"NO_COLOR": "1"})
return Util.cmd_call_pexpect(
ENV_NM_TEST_CLIENT_NMCLI_PATH,
args,
{"NO_COLOR": "1"},
extra_env,
)
@ -2158,6 +2161,55 @@ class TestNmcli(unittest.TestCase):
nmc.pexp.expect(pexpect.EOF)
Util.valgrind_check_log(nmc.valgrind_log, "test_ask_mode")
@Util.skip_without_pexpect
@nm_test
def test_ask_offline(self):
# Make sure we're not using D-Bus
no_dbus_env = {
"DBUS_SYSTEM_BUS_ADDRESS": "very:invalid",
"DBUS_SESSION_BUS_ADDRESS": "very:invalid",
}
nmc = Util.cmd_call_pexpect_nmcli(
["--offline", "--ask", "c", "add"], extra_env=no_dbus_env
)
nmc.pexp.expect("Connection type:")
nmc.pexp.sendline("ethernet")
nmc.pexp.expect("Interface name:")
nmc.pexp.sendline("eth0")
nmc.pexp.expect("There are 3 optional settings for Wired Ethernet.")
nmc.pexp.expect("Do you want to provide them\? \(yes/no\) \[yes]")
nmc.pexp.sendline("no")
nmc.pexp.expect("There are 2 optional settings for IPv4 protocol.")
nmc.pexp.expect("Do you want to provide them\? \(yes/no\) \[yes]")
nmc.pexp.sendline("no")
nmc.pexp.expect("There are 2 optional settings for IPv6 protocol.")
nmc.pexp.expect("Do you want to provide them\? \(yes/no\) \[yes]")
nmc.pexp.sendline("no")
nmc.pexp.expect("There are 4 optional settings for Proxy.")
nmc.pexp.expect("Do you want to provide them\? \(yes/no\) \[yes]")
nmc.pexp.sendline("no")
nmc.pexp.expect(
"\[connection\]\r\n"
+ "id=ethernet\r\n"
+ "uuid=.*\r\n"
+ "type=ethernet\r\n"
+ "interface-name=eth0\r\n"
+ "\r\n"
+ "\[ethernet\]\r\n"
+ "\r\n"
+ "\[ipv4\]\r\n"
+ "method=auto\r\n"
+ "\r\n"
+ "\[ipv6\]\r\n"
+ "addr-gen-mode=default\r\n"
+ "method=auto\r\n"
+ "\r\n"
+ "\[proxy\]\r\n"
)
nmc.pexp.expect(pexpect.EOF)
Util.valgrind_check_log(nmc.valgrind_log, "test_ask_offline")
@Util.skip_without_pexpect
@nm_test
def test_monitor(self):