From 61221ab134ce06363a9f0c20aa3d5829db374a89 Mon Sep 17 00:00:00 2001
From: Srinath Reddy Sadipiralla <srinath.reddy@zohocorp.com>
Date: Fri, 8 Nov 2024 11:10:40 +0530
Subject: [PATCH] Kill psql process immediately if server crashed

---
 src/bin/psql/input.c        |  21 +++++++
 src/bin/psql/input.h        |   3 +
 src/bin/psql/startup.c      | 113 +++++++++++++++++++++++++++++++++---
 src/bin/psql/tab-complete.c |   7 +++
 4 files changed, 136 insertions(+), 8 deletions(-)

diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c
index 01b7ef74c3..3912f00e38 100644
--- a/src/bin/psql/input.c
+++ b/src/bin/psql/input.c
@@ -47,6 +47,9 @@ static int	history_lines_added;
 #define NL_IN_HISTORY	0x01
 #endif
 
+bool waiting_for_input = true;
+pthread_mutex_t waiting_for_input_lock = PTHREAD_MUTEX_INITIALIZER;
+
 static void finishInput(void);
 
 
@@ -87,8 +90,17 @@ gets_interactive(const char *prompt, PQExpBuffer query_buf)
 
 		/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
 		sigint_interrupt_enabled = true;
+		
+		/* Let the thread monitoring psql_fd know that main thread is waiting for user input */
+		pthread_mutex_lock(&waiting_for_input_lock);
+		waiting_for_input = true;
+		pthread_mutex_unlock(&waiting_for_input_lock);
 
 		result = readline(prompt);
+		
+		pthread_mutex_lock(&waiting_for_input_lock);
+		waiting_for_input = false;
+		pthread_mutex_unlock(&waiting_for_input_lock);
 
 		/* Disable SIGINT again */
 		sigint_interrupt_enabled = false;
@@ -201,9 +213,18 @@ gets_fromFile(FILE *source)
 		/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
 		sigint_interrupt_enabled = true;
 
+		/* Let the thread monitoring psql_fd know that main thread is waiting for user input */
+		pthread_mutex_lock(&waiting_for_input_lock);
+		waiting_for_input = true;
+		pthread_mutex_unlock(&waiting_for_input_lock);
+
 		/* Get some data */
 		result = fgets(line, sizeof(line), source);
 
+		pthread_mutex_lock(&waiting_for_input_lock);
+		waiting_for_input = false;
+		pthread_mutex_unlock(&waiting_for_input_lock);
+
 		/* Disable SIGINT again */
 		sigint_interrupt_enabled = false;
 
diff --git a/src/bin/psql/input.h b/src/bin/psql/input.h
index 4c486d67d2..5afb6f58b3 100644
--- a/src/bin/psql/input.h
+++ b/src/bin/psql/input.h
@@ -37,6 +37,9 @@
 
 #include "pqexpbuffer.h"
 
+#include <pthread/pthread.h>
+extern bool waiting_for_input;
+extern pthread_mutex_t waiting_for_input_lock;
 
 extern char *gets_interactive(const char *prompt, PQExpBuffer query_buf);
 extern char *gets_fromFile(FILE *source);
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 036caaec2f..ab352eeb89 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -26,6 +26,13 @@
 #include "mainloop.h"
 #include "settings.h"
 
+#include <pthread.h>
+#include <sys/socket.h>
+
+pthread_t psql_fd_monitor_thread;
+bool main_thread_exited = false;
+pthread_mutex_t main_thread_exited_lock = PTHREAD_MUTEX_INITIALIZER;
+
 /*
  * Global psql options
  */
@@ -117,6 +124,87 @@ empty_signal_handler(SIGNAL_ARGS)
 }
 #endif
 
+static void clean_up()
+{
+	/* clean up */
+	if (pset.logfile)
+		fclose(pset.logfile);
+	if (pset.db)
+		PQfinish(pset.db);
+	if (pset.dead_conn)
+		PQfinish(pset.dead_conn);
+	setQFout(NULL);
+}
+
+static void terminate_process()
+{
+	pthread_mutex_lock(&main_thread_exited_lock);
+	if (!main_thread_exited)
+	{
+		pthread_mutex_unlock(&main_thread_exited_lock);
+		clean_up();
+		kill(getpid(), SIGKILL);
+	}
+	pthread_mutex_unlock(&main_thread_exited_lock);
+	pthread_exit(NULL);
+}
+
+/* Thread function for monitoring the psql's file descriptor */
+static void *psql_fd_monitor_thread_func()
+{
+	pgsocket psql_fd = PQsocket(pset.db);
+	fd_set read_fds;
+	int retval;
+
+	while (1)
+	{
+		/* Check for server process death only if psql is waiting for input */
+		pthread_mutex_lock(&waiting_for_input_lock);
+		if (waiting_for_input)
+		{
+			struct timeval timeout = {};
+			pthread_mutex_unlock(&waiting_for_input_lock);
+
+			FD_ZERO(&read_fds);
+			FD_SET(psql_fd, &read_fds);
+
+			retval = select(psql_fd + 1, &read_fds, NULL, NULL, &timeout);
+			if (retval == -1)
+			{
+				terminate_process();
+			}
+
+			if (FD_ISSET(psql_fd, &read_fds))
+			{
+				char buf;
+				int len = recv(psql_fd, &buf, 1, 0);
+				if (len <= 0)
+				{
+					// If recv fails, we consider it as a trigger to terminate the process
+					terminate_process();
+				}
+			}
+		}
+		else
+		{
+			pthread_mutex_unlock(&waiting_for_input_lock);
+		}
+	}
+
+	return NULL;
+}
+
+static void create_psql_fd_monitor_thread()
+{
+
+	if (pthread_create(&psql_fd_monitor_thread, NULL, psql_fd_monitor_thread_func, NULL) != 0)
+	{
+		pg_log_error("Failed to create fd monitor thread");
+		exit(EXIT_FAILURE);
+	}
+	
+}
+
 /*
  *
  * main
@@ -459,17 +547,26 @@ error:
 		if (!pset.quiet)
 			printf(_("Type \"help\" for help.\n\n"));
 		initializeInput(options.no_readline ? 0 : 1);
+		/* Create a thread to monitor the psql's file descriptor */
+		create_psql_fd_monitor_thread();
 		successResult = MainLoop(stdin);
 	}
 
-	/* clean up */
-	if (pset.logfile)
-		fclose(pset.logfile);
-	if (pset.db)
-		PQfinish(pset.db);
-	if (pset.dead_conn)
-		PQfinish(pset.dead_conn);
-	setQFout(NULL);
+	clean_up();
+
+	pthread_mutex_lock(&main_thread_exited_lock);
+	main_thread_exited = true;
+	pthread_mutex_unlock(&main_thread_exited_lock);
+
+	pthread_mutex_lock(&waiting_for_input_lock);
+	waiting_for_input = true;
+	pthread_mutex_unlock(&waiting_for_input_lock);
+
+	// Wait for the psql's fd monitoring thread to exit
+    if (pthread_join(psql_fd_monitor_thread, NULL) != 0)
+    {
+        pg_log_error("Failed to join psql's fd monitor thread");
+    }
 
 	return successResult;
 }
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 2e55607b8a..023b5096cf 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1752,6 +1752,10 @@ psql_completion(const char *text, int start, int end)
 	char	   *text_copy = pnstrdup(rl_line_buffer + start, end - start);
 	text = text_copy;
 
+	pthread_mutex_lock(&waiting_for_input_lock);
+	waiting_for_input = false;
+	pthread_mutex_unlock(&waiting_for_input_lock);
+
 	/* Remember last char of the given input word. */
 	completion_last_char = (end > start) ? text[end - start - 1] : '\0';
 
@@ -5060,6 +5064,9 @@ psql_completion(const char *text, int start, int end)
 	completion_ref_object = NULL;
 	free(completion_ref_schema);
 	completion_ref_schema = NULL;
+	pthread_mutex_lock(&waiting_for_input_lock);
+	waiting_for_input = true;
+	pthread_mutex_unlock(&waiting_for_input_lock);
 
 	/* Return our Grand List O' Matches */
 	return matches;
-- 
2.39.3 (Apple Git-146)

