From 0150dbdce54f24596547048d4d6617d62a2570a4 Mon Sep 17 00:00:00 2001 From: Bruce Momjian 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 @@ @@ -3587,7 +3587,7 @@ thread-enabled applications. One restriction is that no two threads attempt to manipulate the same 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.) @@ -3612,6 +3612,25 @@ not thread-safe.cryptthread safety It is better to use the md5 method, which is thread-safe on all platforms. + + +libpq must ignore SIGPIPE signals +generated internally by send() calls to backend processes. +When PostgreSQL is configured without +--enable-thread-safety, libpq sets +SIGPIPE to SIG_IGN before each +send() call and restores the original signal handler after +completion. When --enable-thread-safety is used, +libpq installs its own SIGPIPE handler +before the first database connection if no custom SIGPIPE +handler has been installed previously. This handler uses thread-local +storage to determine if a SIGPIPE signal has been generated +by an internal send(). If an application wants to install +its own SIGPIPE signal handler, it should call +PQinSend() to determine if it should ignore the +SIGPIPE signal. This function is available in both +thread-safe and non-thread-safe versions of libpq. + 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 #include +#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 #endif +#ifdef ENABLE_THREAD_SAFETY +#include +#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 #endif +#ifdef ENABLE_THREAD_SAFETY +#include +#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 #endif +#ifdef ENABLE_THREAD_SAFETY +#include +#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 */ -- 2.39.5