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