From 0150dbdce54f24596547048d4d6617d62a2570a4 Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Fri, 9 Jan 2004 02:02:43 +0000
Subject: [PATCH] Allow libpq to do thread-safe SIGPIPE handling.  This allows
 it to ignore SIGPIPE from send() in libpq, but terminate on any other
 SIGPIPE, unless the user installs their own signal handler.

This is a minor fix because the only time you get SIGPIPE from libpq's
send() is when the backend dies.
---
 doc/src/sgml/libpq.sgml           | 23 +++++++++-
 src/backend/nodes/read.c          |  3 +-
 src/interfaces/libpq/fe-connect.c | 15 ++++++-
 src/interfaces/libpq/fe-print.c   | 16 ++++++-
 src/interfaces/libpq/fe-secure.c  | 75 ++++++++++++++++++++++++++++++-
 src/interfaces/libpq/libpq-fe.h   | 10 ++++-
 src/interfaces/libpq/libpq-int.h  |  9 +++-
 src/interfaces/libpq/pqsignal.c   | 24 +++++++++-
 src/interfaces/libpq/pqsignal.h   |  4 +-
 9 files changed, 167 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 248d8646af2..bfd66945f9c 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.144 2003/12/13 23:59:06 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.145 2004/01/09 02:02:43 momjian Exp $
 -->
 
  <chapter id="libpq">
@@ -3587,7 +3587,7 @@ thread-enabled applications.
 One restriction is that no two threads attempt to manipulate the same
 <structname>PGconn</> object at the same time. In particular, you cannot
 issue concurrent commands from different threads through the same
-connection object. (If you need to run concurrent commands, start up
+connection object. (If you need to run concurrent commands, use
 multiple connections.)
 </para>
 
@@ -3612,6 +3612,25 @@ not thread-safe.<indexterm><primary>crypt</><secondary>thread
 safety</></> It is better to use the <literal>md5</literal> method,
 which is thread-safe on all platforms.
 </para>
+
+<para>
+<application>libpq</application> must ignore <literal>SIGPIPE</> signals
+generated internally by <function>send()</> calls to backend processes.
+When <productname>PostgreSQL</> is configured without
+<literal>--enable-thread-safety</>, <application>libpq</> sets
+<literal>SIGPIPE</> to <literal>SIG_IGN</> before each
+<function>send()</> call and restores the original signal handler after
+completion. When <literal>--enable-thread-safety</> is used,
+<application>libpq</> installs its own <literal>SIGPIPE</> handler
+before the first database connection if no custom <literal>SIGPIPE</>
+handler has been installed previously. This handler uses thread-local
+storage to determine if a <literal>SIGPIPE</> signal has been generated
+by an internal <function>send()</>. If an application wants to install
+its own <literal>SIGPIPE</> signal handler, it should call
+<function>PQinSend()</> to determine if it should ignore the
+<literal>SIGPIPE</> signal. This function is available in both
+thread-safe and non-thread-safe versions of <application>libpq</>.
+</para>
 </sect1>
 
 
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index 15abc4ac426..7be2ff403b4 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/read.c,v 1.37 2004/01/07 21:12:56 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/read.c,v 1.38 2004/01/09 02:02:43 momjian Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -22,6 +22,7 @@
 #include <ctype.h>
 #include <errno.h>
 
+#include "nodes/value.h"
 #include "nodes/pg_list.h"
 #include "nodes/readfuncs.h"
 #include "nodes/value.h"
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 233d3a2fe16..1086fd76320 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.266 2004/01/07 18:56:29 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.267 2004/01/09 02:02:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,6 +43,10 @@
 #include <arpa/inet.h>
 #endif
 
+#ifdef ENABLE_THREAD_SAFETY
+#include <pthread.h>
+#endif
+
 #include "libpq/ip.h"
 #include "mb/pg_wchar.h"
 
@@ -66,7 +70,6 @@ long		ioctlsocket_ret=1;
 #define DefaultSSLMode	"disable"
 #endif
 
-
 /* ----------
  * Definition of the conninfo parameters and their fallback resources.
  *
@@ -198,6 +201,7 @@ static char *pwdfMatchesString(char *buf, char *token);
 static char *PasswordFromFile(char *hostname, char *port, char *dbname,
 				 char *username);
 
+
 /*
  *		Connecting to a Database
  *
@@ -881,6 +885,12 @@ connectDBStart(PGconn *conn)
 	struct addrinfo hint;
 	const char *node = NULL;
 	int			ret;
+#ifdef ENABLE_THREAD_SAFETY
+	static pthread_once_t check_sigpipe_once = PTHREAD_ONCE_INIT;
+
+	/* Check only on first connection request */
+	pthread_once(&check_sigpipe_once, check_sigpipe_handler);
+#endif
 
 	if (!conn)
 		return 0;
@@ -3158,3 +3168,4 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
 
 #undef LINELEN
 }
+
diff --git a/src/interfaces/libpq/fe-print.c b/src/interfaces/libpq/fe-print.c
index b7fa71b1b72..9afa1294e6f 100644
--- a/src/interfaces/libpq/fe-print.c
+++ b/src/interfaces/libpq/fe-print.c
@@ -10,7 +10,7 @@
  * didn't really belong there.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.49 2003/11/29 19:52:12 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.50 2004/01/09 02:02:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -90,8 +90,10 @@ PQprint(FILE *fout,
 		int			fs_len = strlen(po->fieldSep);
 		int			total_line_length = 0;
 		int			usePipe = 0;
-		pqsigfunc	oldsigpipehandler = NULL;
 		char	   *pagerenv;
+#if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
+		pqsigfunc	oldsigpipehandler = NULL;
+#endif
 
 #ifdef TIOCGWINSZ
 		struct winsize screen_size;
@@ -189,8 +191,12 @@ PQprint(FILE *fout,
 				if (fout)
 				{
 					usePipe = 1;
+#ifdef ENABLE_THREAD_SAFETY
+					pthread_setspecific(thread_in_send, "t");
+#else
 #ifndef WIN32
 					oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
+#endif
 #endif
 				}
 				else
@@ -306,7 +312,13 @@ PQprint(FILE *fout,
 			_pclose(fout);
 #else
 			pclose(fout);
+#endif
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_setspecific(thread_in_send, "f");
+#else
+#ifndef WIN32
 			pqsignal(SIGPIPE, oldsigpipehandler);
+#endif
 #endif
 		}
 		if (po->html3 && !po->expanded)
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 34f94b0ad07..e650b6b275e 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.34 2003/12/18 22:49:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.35 2004/01/09 02:02:43 momjian Exp $
  *
  * NOTES
  *	  The client *requires* a valid server certificate.  Since
@@ -106,6 +106,10 @@
 #include <arpa/inet.h>
 #endif
 
+#ifdef ENABLE_THREAD_SAFETY
+#include <pthread.h>
+#endif
+
 #ifndef HAVE_STRDUP
 #include "strdup.h"
 #endif
@@ -142,6 +146,11 @@ static const char *SSLerrmessage(void);
 static SSL_CTX *SSL_context = NULL;
 #endif
 
+#ifdef ENABLE_THREAD_SAFETY
+static void sigpipe_handler_ignore_send(int signo);
+pthread_key_t thread_in_send;
+#endif
+
 /* ------------------------------------------------------------ */
 /*						 Hardcoded values						*/
 /* ------------------------------------------------------------ */
@@ -347,9 +356,13 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 {
 	ssize_t		n;
 
+#ifdef ENABLE_THREAD_SAFETY
+	pthread_setspecific(thread_in_send, "t");
+#else
 #ifndef WIN32
 	pqsigfunc	oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
 #endif
+#endif
 
 #ifdef USE_SSL
 	if (conn->ssl)
@@ -407,8 +420,12 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 #endif
 		n = send(conn->sock, ptr, len, 0);
 
+#ifdef ENABLE_THREAD_SAFETY
+	pthread_setspecific(thread_in_send, "f");
+#else
 #ifndef WIN32
 	pqsignal(SIGPIPE, oldsighandler);
+#endif
 #endif
 
 	return n;
@@ -1048,3 +1065,59 @@ PQgetssl(PGconn *conn)
 }
 
 #endif   /* USE_SSL */
+
+
+#ifdef ENABLE_THREAD_SAFETY
+/*
+ *	Check SIGPIPE handler and perhaps install our own.
+ */
+void
+check_sigpipe_handler(void)
+{
+	pqsigfunc pipehandler;
+
+	/*
+	 *	If the app hasn't set a SIGPIPE handler, define our own
+	 *	that ignores SIGPIPE on libpq send() and does SIG_DFL
+	 *	for other SIGPIPE cases.
+	 */
+	pipehandler = pqsignalinquire(SIGPIPE);
+	if (pipehandler == SIG_DFL)	/* not set by application */
+	{
+		/*
+		 *	Create key first because the signal handler might be called
+		 *	right after being installed.
+		 */
+		pthread_key_create(&thread_in_send, NULL);	
+		pqsignal(SIGPIPE, sigpipe_handler_ignore_send);
+	}
+}
+
+/*
+ *	Threaded SIGPIPE signal handler
+ */
+void
+sigpipe_handler_ignore_send(int signo)
+{
+	/* If we have gotten a SIGPIPE outside send(), exit */
+	if (!PQinSend())
+		exit(128 + SIGPIPE);	/* typical return value for SIG_DFL */
+}
+#endif
+ 
+/*
+ *	Indicates whether the current thread is in send()
+ *	For use by SIGPIPE signal handlers;  they should
+ *	ignore SIGPIPE when libpq is in send().  This means
+ *	that the backend has died unexpectedly.
+ */
+pqbool
+PQinSend(void)
+{
+#ifdef ENABLE_THREAD_SAFETY
+	return (pthread_getspecific(thread_in_send) /* has it been set? */ &&
+			*(char *)pthread_getspecific(thread_in_send) == 't') ? true : false;
+#else
+	return false;	/* No threading, so we can't be in send() */
+#endif
+}
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 4b4eb9ab4d3..f6ea1f4ba74 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.101 2003/11/29 22:41:28 pgsql Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.102 2004/01/09 02:02:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -450,6 +450,14 @@ extern int	PQmblen(const unsigned char *s, int encoding);
 /* Get encoding id from environment variable PGCLIENTENCODING */
 extern int	PQenv2encoding(void);
 
+/* === in fe-secure.c === */
+
+/*
+ *	Indicates whether the libpq thread is in send().
+ *	Used to ignore SIGPIPE if thread is in send().
+ */
+pqbool PQinSend(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index fff2bf5d674..97cd55eaa8a 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.83 2003/11/29 22:41:28 pgsql Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.84 2004/01/09 02:02:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,9 @@
 #include <sys/time.h>
 #endif
 
+#ifdef ENABLE_THREAD_SAFETY
+#include <pthread.h>
+#endif
 
 #if defined(WIN32) && (!defined(ssize_t))
 typedef int ssize_t;			/* ssize_t doesn't exist in VC (at least
@@ -442,6 +445,10 @@ extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
 extern void pqsecure_close(PGconn *);
 extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
+#ifdef ENABLE_THREAD_SAFETY
+extern void check_sigpipe_handler(void);
+extern pthread_key_t thread_in_send;
+#endif
 
 /*
  * this is so that we can check if a connection is non-blocking internally
diff --git a/src/interfaces/libpq/pqsignal.c b/src/interfaces/libpq/pqsignal.c
index 179a3a99aa1..5f41d228103 100644
--- a/src/interfaces/libpq/pqsignal.c
+++ b/src/interfaces/libpq/pqsignal.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/interfaces/libpq/pqsignal.c,v 1.18 2003/11/29 19:52:12 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/interfaces/libpq/pqsignal.c,v 1.19 2004/01/09 02:02:43 momjian Exp $
  *
  * NOTES
  *		This shouldn't be in libpq, but the monitor and some other
@@ -40,3 +40,25 @@ pqsignal(int signo, pqsigfunc func)
 	return oact.sa_handler;
 #endif   /* !HAVE_POSIX_SIGNALS */
 }
+
+pqsigfunc
+pqsignalinquire(int signo)
+{
+#if !defined(HAVE_POSIX_SIGNALS)
+	pqsigfunc old_sigfunc;
+	int		old_sigmask;
+
+	/* Prevent signal handler calls during test */
+	old_sigmask = sigblock(sigmask(signo));
+ 	old_sigfunc = signal(signo, SIG_DFL);
+	signal(signo, old_sigfunc);
+	sigblock(old_sigmask);
+	return old_sigfunc;
+#else
+	struct sigaction oact;
+
+	if (sigaction(signo, NULL, &oact) < 0)
+       return SIG_ERR;
+	return oact.sa_handler;
+#endif   /* !HAVE_POSIX_SIGNALS */
+}
diff --git a/src/interfaces/libpq/pqsignal.h b/src/interfaces/libpq/pqsignal.h
index 3dcb8a8b956..a948802022a 100644
--- a/src/interfaces/libpq/pqsignal.h
+++ b/src/interfaces/libpq/pqsignal.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/pqsignal.h,v 1.16 2003/11/29 22:41:28 pgsql Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/pqsignal.h,v 1.17 2004/01/09 02:02:43 momjian Exp $
  *
  * NOTES
  *	  This shouldn't be in libpq, but the monitor and some other
@@ -24,4 +24,6 @@ typedef void (*pqsigfunc) (int);
 
 extern pqsigfunc pqsignal(int signo, pqsigfunc func);
 
+extern pqsigfunc pqsignalinquire(int signo);
+
 #endif   /* PQSIGNAL_H */
-- 
GitLab