From d414265ab17c54f7b5bc27a55536ca99c77ee80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Huguet?= Date: Mon, 17 Jul 2023 10:56:35 +0200 Subject: [PATCH] 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. --- src/nmcli/common.c | 5 +-- src/tests/client/test-client.py | 56 +++++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/nmcli/common.c b/src/nmcli/common.c index 01fe9a375f..2e8ded7e44 100644 --- a/src/nmcli/common.c +++ b/src/nmcli/common.c @@ -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(); } diff --git a/src/tests/client/test-client.py b/src/tests/client/test-client.py index 391c14d1c3..b618fb9703 100755 --- a/src/tests/client/test-client.py +++ b/src/tests/client/test-client.py @@ -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):