git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 1/2] terminal: teach git how to save/restore its terminal settings

From
Carlo Marcelo Arenas Belón <carenas@gmail.com>
Date
Oct 5, 2021, 07:46 UTC
Message-ID
<20211005074648.86032-2-carenas@gmail.com>
In-Reply-To
<20211005074648.86032-1-carenas@gmail.com>

Currently, git will share its console with all its children (unless they create their own), and is therefore possible that any of them that might change the settings for it could affect its operations once completed.

Refactor the platform specific functionality to save the terminal settings and expand it to also do so for the output handler.

This will allow for the state of the terminal to be saved and restored around a child that might misbehave (ex vi) which will be implemented next.

Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
v2:
- remove unnecessary forward declaration for restore_term per Junio
- reword first paragraph with hopefully better grammar
 compat/terminal.c | 75 +++++++++++++++++++++++++++++++++++++----------
 compat/terminal.h |  3 ++
 2 files changed, 63 insertions(+), 15 deletions(-)
diff --git a/compat/terminal.c b/compat/terminal.c
index 43b73ddc75..5b903e7c7e 100644
--- a/compat/terminal.c
+++ b/compat/terminal.c
@@ -8,8 +8,6 @@
 
 #if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
 
-static void restore_term(void);
-
 static void restore_term_on_signal(int sig)
 {
 	restore_term();
@@ -25,7 +23,7 @@ static void restore_term_on_signal(int sig)
 static int term_fd = -1;
 static struct termios old_term;
 
-static void restore_term(void)
+void restore_term(void)
 {
 	if (term_fd < 0)
 		return;
@@ -35,15 +33,22 @@ static void restore_term(void)
 	term_fd = -1;
 }
 
+int save_term(int full_duplex)
+{
+	if (term_fd < 0)
+		term_fd = open("/dev/tty", O_RDWR);
+
+	return (term_fd < 0) ? -1 : tcgetattr(term_fd, &old_term);
+}
+
 static int disable_bits(tcflag_t bits)
 {
 	struct termios t;
 
-	term_fd = open("/dev/tty", O_RDWR);
-	if (tcgetattr(term_fd, &t) < 0)
+	if (save_term(0) < 0)
 		goto error;
 
-	old_term = t;
+	t = old_term;
 	sigchain_push_common(restore_term_on_signal);
 
 	t.c_lflag &= ~bits;
@@ -75,9 +80,10 @@ static int enable_non_canonical(void)
 static int use_stty = 1;
 static struct string_list stty_restore = STRING_LIST_INIT_DUP;
 static HANDLE hconin = INVALID_HANDLE_VALUE;
-static DWORD cmode;
+static HANDLE hconout = INVALID_HANDLE_VALUE;
+static DWORD cmode_in, cmode_out;
 
-static void restore_term(void)
+void restore_term(void)
 {
 	if (use_stty) {
 		int i;
@@ -97,9 +103,42 @@ static void restore_term(void)
 	if (hconin == INVALID_HANDLE_VALUE)
 		return;
 
-	SetConsoleMode(hconin, cmode);
+	SetConsoleMode(hconin, cmode_in);
+	CloseHandle(hconin);
+	if (cmode_out) {
+		assert(hconout != INVALID_HANDLE_VALUE);
+		SetConsoleMode(hconout, cmode_out);
+		CloseHandle(hconout);
+	}
+
+	hconin = hconout = INVALID_HANDLE_VALUE;
+}
+
+int save_term(int full_duplex)
+{
+	hconin = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE,
+	    FILE_SHARE_READ, NULL, OPEN_EXISTING,
+	    FILE_ATTRIBUTE_NORMAL, NULL);
+	if (hconin == INVALID_HANDLE_VALUE)
+		return -1;
+
+	if (full_duplex) {
+		hconout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE,
+			FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+			FILE_ATTRIBUTE_NORMAL, NULL);
+		if (hconout == INVALID_HANDLE_VALUE)
+			goto error;
+
+		GetConsoleMode(hconout, &cmode_out);
+	}
+
+	GetConsoleMode(hconin, &cmode_in);
+	use_stty = 0;
+	return 0;
+error:
 	CloseHandle(hconin);
 	hconin = INVALID_HANDLE_VALUE;
+	return -1;
 }
 
 static int disable_bits(DWORD bits)
@@ -135,15 +174,11 @@ static int disable_bits(DWORD bits)
 		use_stty = 0;
 	}
 
-	hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
-	    FILE_SHARE_READ, NULL, OPEN_EXISTING,
-	    FILE_ATTRIBUTE_NORMAL, NULL);
-	if (hconin == INVALID_HANDLE_VALUE)
+	if (save_term(0) < 0)
 		return -1;
 
-	GetConsoleMode(hconin, &cmode);
 	sigchain_push_common(restore_term_on_signal);
-	if (!SetConsoleMode(hconin, cmode & ~bits)) {
+	if (!SetConsoleMode(hconin, cmode_in & ~bits)) {
 		CloseHandle(hconin);
 		hconin = INVALID_HANDLE_VALUE;
 		return -1;
@@ -361,6 +396,16 @@ int read_key_without_echo(struct strbuf *buf)
 
 #else
 
+int save_term(int full_duplex)
+{
+	/* full_duplex == 1, but no support available */
+	return -full_duplex;
+}
+
+void restore_term(void)
+{
+}
+
 char *git_terminal_prompt(const char *prompt, int echo)
 {
 	return getpass(prompt);
diff --git a/compat/terminal.h b/compat/terminal.h
index a9d52b8464..e1770c575b 100644
--- a/compat/terminal.h
+++ b/compat/terminal.h
@@ -1,6 +1,9 @@
 #ifndef COMPAT_TERMINAL_H
 #define COMPAT_TERMINAL_H
 
+int save_term(int full_duplex);
+void restore_term(void);
+
 char *git_terminal_prompt(const char *prompt, int echo);
 
 /* Read a single keystroke, without echoing it to the terminal */
-- 
2.33.0.955.gee03ddbf0e
Previous: Carlo Marcelo Arenas BelónNext: Carlo Marcelo Arenas Belón
Message 13 of 14 in “protect git from a rogue editor”
  1. 0/2 protect git from a rogue editorCarlo Marcelo Arenas Belón, Oct 2, 2021
  2. 1/2 terminal: teach git how to save/restore its terminal settingsCarlo Marcelo Arenas Belón, Oct 2, 2021
  3. 2/2 editor: save and reset terminal after calling EDITORCarlo Marcelo Arenas Belón, Oct 2, 2021
  4. 0/2 protect git from a rogue editorCarlo Marcelo Arenas Belón, Oct 4, 2021
  5. 1/2 terminal: teach git how to save/restore its terminal settingsCarlo Marcelo Arenas Belón, Oct 4, 2021
  6. Junio C HamanoOct 4, 2021
  7. Carlo ArenasOct 4, 2021
  8. Junio C HamanoOct 4, 2021
  9. Carlo ArenasOct 4, 2021
  10. 2/2 editor: save and reset terminal after calling EDITORCarlo Marcelo Arenas Belón, Oct 4, 2021
  11. Junio C HamanoOct 4, 2021
  12. 0/2 protect git from a rogue editorCarlo Marcelo Arenas Belón, Oct 5, 2021
  13. 1/2 terminal: teach git how to save/restore its terminal settingsCarlo Marcelo Arenas Belón, Oct 5, 2021
  14. 2/2 editor: save and reset terminal after calling EDITORCarlo Marcelo Arenas Belón, Oct 5, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.