Commit 0150dbdc authored by Bruce Momjian's avatar Bruce Momjian

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.
parent acc57543
<!-- <!--
$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"> <chapter id="libpq">
...@@ -3587,7 +3587,7 @@ thread-enabled applications. ...@@ -3587,7 +3587,7 @@ thread-enabled applications.
One restriction is that no two threads attempt to manipulate the same One restriction is that no two threads attempt to manipulate the same
<structname>PGconn</> object at the same time. In particular, you cannot <structname>PGconn</> object at the same time. In particular, you cannot
issue concurrent commands from different threads through the same 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.) multiple connections.)
</para> </para>
...@@ -3612,6 +3612,25 @@ not thread-safe.<indexterm><primary>crypt</><secondary>thread ...@@ -3612,6 +3612,25 @@ not thread-safe.<indexterm><primary>crypt</><secondary>thread
safety</></> It is better to use the <literal>md5</literal> method, safety</></> It is better to use the <literal>md5</literal> method,
which is thread-safe on all platforms. which is thread-safe on all platforms.
</para> </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> </sect1>
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include "nodes/value.h"
#include "nodes/pg_list.h" #include "nodes/pg_list.h"
#include "nodes/readfuncs.h" #include "nodes/readfuncs.h"
#include "nodes/value.h" #include "nodes/value.h"
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -43,6 +43,10 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
#ifdef ENABLE_THREAD_SAFETY
#include <pthread.h>
#endif
#include "libpq/ip.h" #include "libpq/ip.h"
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
...@@ -66,7 +70,6 @@ long ioctlsocket_ret=1; ...@@ -66,7 +70,6 @@ long ioctlsocket_ret=1;
#define DefaultSSLMode "disable" #define DefaultSSLMode "disable"
#endif #endif
/* ---------- /* ----------
* Definition of the conninfo parameters and their fallback resources. * Definition of the conninfo parameters and their fallback resources.
* *
...@@ -198,6 +201,7 @@ static char *pwdfMatchesString(char *buf, char *token); ...@@ -198,6 +201,7 @@ static char *pwdfMatchesString(char *buf, char *token);
static char *PasswordFromFile(char *hostname, char *port, char *dbname, static char *PasswordFromFile(char *hostname, char *port, char *dbname,
char *username); char *username);
/* /*
* Connecting to a Database * Connecting to a Database
* *
...@@ -881,6 +885,12 @@ connectDBStart(PGconn *conn) ...@@ -881,6 +885,12 @@ connectDBStart(PGconn *conn)
struct addrinfo hint; struct addrinfo hint;
const char *node = NULL; const char *node = NULL;
int ret; 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) if (!conn)
return 0; return 0;
...@@ -3158,3 +3168,4 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username) ...@@ -3158,3 +3168,4 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
#undef LINELEN #undef LINELEN
} }
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* didn't really belong there. * didn't really belong there.
* *
* IDENTIFICATION * 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, ...@@ -90,8 +90,10 @@ PQprint(FILE *fout,
int fs_len = strlen(po->fieldSep); int fs_len = strlen(po->fieldSep);
int total_line_length = 0; int total_line_length = 0;
int usePipe = 0; int usePipe = 0;
pqsigfunc oldsigpipehandler = NULL;
char *pagerenv; char *pagerenv;
#if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
pqsigfunc oldsigpipehandler = NULL;
#endif
#ifdef TIOCGWINSZ #ifdef TIOCGWINSZ
struct winsize screen_size; struct winsize screen_size;
...@@ -189,8 +191,12 @@ PQprint(FILE *fout, ...@@ -189,8 +191,12 @@ PQprint(FILE *fout,
if (fout) if (fout)
{ {
usePipe = 1; usePipe = 1;
#ifdef ENABLE_THREAD_SAFETY
pthread_setspecific(thread_in_send, "t");
#else
#ifndef WIN32 #ifndef WIN32
oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN); oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
#endif
#endif #endif
} }
else else
...@@ -306,7 +312,13 @@ PQprint(FILE *fout, ...@@ -306,7 +312,13 @@ PQprint(FILE *fout,
_pclose(fout); _pclose(fout);
#else #else
pclose(fout); pclose(fout);
#endif
#ifdef ENABLE_THREAD_SAFETY
pthread_setspecific(thread_in_send, "f");
#else
#ifndef WIN32
pqsignal(SIGPIPE, oldsigpipehandler); pqsignal(SIGPIPE, oldsigpipehandler);
#endif
#endif #endif
} }
if (po->html3 && !po->expanded) if (po->html3 && !po->expanded)
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* The client *requires* a valid server certificate. Since * The client *requires* a valid server certificate. Since
...@@ -106,6 +106,10 @@ ...@@ -106,6 +106,10 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
#ifdef ENABLE_THREAD_SAFETY
#include <pthread.h>
#endif
#ifndef HAVE_STRDUP #ifndef HAVE_STRDUP
#include "strdup.h" #include "strdup.h"
#endif #endif
...@@ -142,6 +146,11 @@ static const char *SSLerrmessage(void); ...@@ -142,6 +146,11 @@ static const char *SSLerrmessage(void);
static SSL_CTX *SSL_context = NULL; static SSL_CTX *SSL_context = NULL;
#endif #endif
#ifdef ENABLE_THREAD_SAFETY
static void sigpipe_handler_ignore_send(int signo);
pthread_key_t thread_in_send;
#endif
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/* Hardcoded values */ /* Hardcoded values */
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
...@@ -347,9 +356,13 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len) ...@@ -347,9 +356,13 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
{ {
ssize_t n; ssize_t n;
#ifdef ENABLE_THREAD_SAFETY
pthread_setspecific(thread_in_send, "t");
#else
#ifndef WIN32 #ifndef WIN32
pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN); pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
#endif #endif
#endif
#ifdef USE_SSL #ifdef USE_SSL
if (conn->ssl) if (conn->ssl)
...@@ -407,8 +420,12 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len) ...@@ -407,8 +420,12 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
#endif #endif
n = send(conn->sock, ptr, len, 0); n = send(conn->sock, ptr, len, 0);
#ifdef ENABLE_THREAD_SAFETY
pthread_setspecific(thread_in_send, "f");
#else
#ifndef WIN32 #ifndef WIN32
pqsignal(SIGPIPE, oldsighandler); pqsignal(SIGPIPE, oldsighandler);
#endif
#endif #endif
return n; return n;
...@@ -1048,3 +1065,59 @@ PQgetssl(PGconn *conn) ...@@ -1048,3 +1065,59 @@ PQgetssl(PGconn *conn)
} }
#endif /* USE_SSL */ #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
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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); ...@@ -450,6 +450,14 @@ extern int PQmblen(const unsigned char *s, int encoding);
/* Get encoding id from environment variable PGCLIENTENCODING */ /* Get encoding id from environment variable PGCLIENTENCODING */
extern int PQenv2encoding(void); 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 #ifdef __cplusplus
} }
#endif #endif
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -29,6 +29,9 @@
#include <sys/time.h> #include <sys/time.h>
#endif #endif
#ifdef ENABLE_THREAD_SAFETY
#include <pthread.h>
#endif
#if defined(WIN32) && (!defined(ssize_t)) #if defined(WIN32) && (!defined(ssize_t))
typedef int ssize_t; /* ssize_t doesn't exist in VC (at least typedef int ssize_t; /* ssize_t doesn't exist in VC (at least
...@@ -442,6 +445,10 @@ extern PostgresPollingStatusType pqsecure_open_client(PGconn *); ...@@ -442,6 +445,10 @@ extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
extern void pqsecure_close(PGconn *); extern void pqsecure_close(PGconn *);
extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len); extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
extern ssize_t pqsecure_write(PGconn *, const 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 * this is so that we can check if a connection is non-blocking internally
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* This shouldn't be in libpq, but the monitor and some other * This shouldn't be in libpq, but the monitor and some other
...@@ -40,3 +40,25 @@ pqsignal(int signo, pqsigfunc func) ...@@ -40,3 +40,25 @@ pqsignal(int signo, pqsigfunc func)
return oact.sa_handler; return oact.sa_handler;
#endif /* !HAVE_POSIX_SIGNALS */ #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 */
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* This shouldn't be in libpq, but the monitor and some other * This shouldn't be in libpq, but the monitor and some other
...@@ -24,4 +24,6 @@ typedef void (*pqsigfunc) (int); ...@@ -24,4 +24,6 @@ typedef void (*pqsigfunc) (int);
extern pqsigfunc pqsignal(int signo, pqsigfunc func); extern pqsigfunc pqsignal(int signo, pqsigfunc func);
extern pqsigfunc pqsignalinquire(int signo);
#endif /* PQSIGNAL_H */ #endif /* PQSIGNAL_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment