diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index d20d0b601063efb032997ef7aaa3b4e3445e4fbd..41c753a4f8cdb172fb0a17265ab559d098639adb 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.375 2009/06/11 14:49:13 momjian Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.376 2009/07/24 17:58:31 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1018,6 +1018,7 @@ PQconnectPoll(PGconn *conn) { PGresult *res; char sebuf[256]; + int optval; if (conn == NULL) return PGRES_POLLING_FAILED; @@ -1153,6 +1154,46 @@ keep_going: /* We will come back to here until there is } #endif /* F_SETFD */ + /*---------- + * We have three methods of blocking SIGPIPE during + * send() calls to this socket: + * + * - setsockopt(sock, SO_NOSIGPIPE) + * - send(sock, ..., MSG_NOSIGNAL) + * - setting the signal mask to SIG_IGN during send() + * + * The third method requires three syscalls per send, + * so we prefer either of the first two, but they are + * less portable. The state is tracked in the following + * members of PGconn: + * + * conn->sigpipe_so - we have set up SO_NOSIGPIPE + * conn->sigpipe_flag - we're specifying MSG_NOSIGNAL + * + * If we can use SO_NOSIGPIPE, then set sigpipe_so here + * and we're done. Otherwise, set sigpipe_flag so that + * we will try MSG_NOSIGNAL on sends. If we get an error + * with MSG_NOSIGNAL, we'll clear that flag and revert to + * signal masking. + *---------- + */ + conn->sigpipe_so = false; +#ifdef MSG_NOSIGNAL + conn->sigpipe_flag = true; +#else + conn->sigpipe_flag = false; +#endif /* MSG_NOSIGNAL */ + +#ifdef SO_NOSIGPIPE + optval = 1; + if (setsockopt(conn->sock, SOL_SOCKET, SO_NOSIGPIPE, + (char *) &optval, sizeof(optval)) == 0) + { + conn->sigpipe_so = true; + conn->sigpipe_flag = false; + } +#endif /* SO_NOSIGPIPE */ + /* * Start/make connection. This should not block, since we * are in nonblock mode. If it does, well, too bad. @@ -1214,7 +1255,6 @@ keep_going: /* We will come back to here until there is case CONNECTION_STARTED: { - int optval; ACCEPT_TYPE_ARG3 optlen = sizeof(optval); /* diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c index c6e64147872ad45232c46c7d30513845d75d705c..dbd37d50390d7aa6a92591dd2289151442c4b5d0 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.127 2009/06/23 18:13:23 mha Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.128 2009/07/24 17:58:31 tgl Exp $ * * NOTES * @@ -118,44 +118,76 @@ static long win32_ssl_create_mutex = 0; /* * Macros to handle disabling and then restoring the state of SIGPIPE handling. - * Note that DISABLE_SIGPIPE() must appear at the start of a block. + * On Windows, these are all no-ops since there's no SIGPIPEs. */ #ifndef WIN32 + +#define SIGPIPE_MASKED(conn) ((conn)->sigpipe_so || (conn)->sigpipe_flag) + #ifdef ENABLE_THREAD_SAFETY -#define DISABLE_SIGPIPE(failaction) \ - sigset_t osigmask; \ - bool sigpipe_pending; \ - bool got_epipe = false; \ -\ - if (pq_block_sigpipe(&osigmask, &sigpipe_pending) < 0) \ - failaction +struct sigpipe_info +{ + sigset_t oldsigmask; + bool sigpipe_pending; + bool got_epipe; +}; -#define REMEMBER_EPIPE(cond) \ +#define DECLARE_SIGPIPE_INFO(spinfo) struct sigpipe_info spinfo + +#define DISABLE_SIGPIPE(conn, spinfo, failaction) \ + do { \ + (spinfo).got_epipe = false; \ + if (!SIGPIPE_MASKED(conn)) \ + { \ + if (pq_block_sigpipe(&(spinfo).oldsigmask, \ + &(spinfo).sigpipe_pending) < 0) \ + failaction; \ + } \ + } while (0) + +#define REMEMBER_EPIPE(spinfo, cond) \ do { \ if (cond) \ - got_epipe = true; \ + (spinfo).got_epipe = true; \ } while (0) -#define RESTORE_SIGPIPE() \ - pq_reset_sigpipe(&osigmask, sigpipe_pending, got_epipe) -#else /* !ENABLE_THREAD_SAFETY */ +#define RESTORE_SIGPIPE(conn, spinfo) \ + do { \ + if (!SIGPIPE_MASKED(conn)) \ + pq_reset_sigpipe(&(spinfo).oldsigmask, (spinfo).sigpipe_pending, \ + (spinfo).got_epipe); \ + } while (0) -#define DISABLE_SIGPIPE(failaction) \ - pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN) +#else /* !ENABLE_THREAD_SAFETY */ -#define REMEMBER_EPIPE(cond) +#define DECLARE_SIGPIPE_INFO(spinfo) pqsigfunc spinfo = NULL -#define RESTORE_SIGPIPE() \ - pqsignal(SIGPIPE, oldsighandler) -#endif /* ENABLE_THREAD_SAFETY */ -#else /* WIN32 */ +#define DISABLE_SIGPIPE(conn, spinfo, failaction) \ + do { \ + if (!SIGPIPE_MASKED(conn)) \ + spinfo = pqsignal(SIGPIPE, SIG_IGN); \ + } while (0) + +#define REMEMBER_EPIPE(spinfo, cond) + +#define RESTORE_SIGPIPE(conn, spinfo) \ + do { \ + if (!SIGPIPE_MASKED(conn)) \ + pqsignal(SIGPIPE, spinfo); \ + } while (0) + +#endif /* ENABLE_THREAD_SAFETY */ -#define DISABLE_SIGPIPE(failaction) -#define REMEMBER_EPIPE(cond) -#define RESTORE_SIGPIPE() -#endif /* WIN32 */ +#else /* WIN32 */ + +#define DECLARE_SIGPIPE_INFO(spinfo) +#define DISABLE_SIGPIPE(conn, spinfo, failaction) +#define REMEMBER_EPIPE(spinfo, cond) +#define RESTORE_SIGPIPE(conn, spinfo) + +#endif /* WIN32 */ /* ------------------------------------------------------------ */ /* Procedures common to all secure sessions */ @@ -231,6 +263,9 @@ pqsecure_open_client(PGconn *conn) /* First time through? */ if (conn->ssl == NULL) { + /* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */ + conn->sigpipe_flag = false; + if (!(conn->ssl = SSL_new(SSL_context)) || !SSL_set_app_data(conn->ssl, conn) || !SSL_set_fd(conn->ssl, conn->sock)) @@ -283,9 +318,10 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len) if (conn->ssl) { int err; + DECLARE_SIGPIPE_INFO(spinfo); /* SSL_read can write to the socket, so we need to disable SIGPIPE */ - DISABLE_SIGPIPE(return -1); + DISABLE_SIGPIPE(conn, spinfo, return -1); rloop: n = SSL_read(conn->ssl, ptr, len); @@ -312,7 +348,7 @@ rloop: if (n == -1) { - REMEMBER_EPIPE(SOCK_ERRNO == EPIPE); + REMEMBER_EPIPE(spinfo, SOCK_ERRNO == EPIPE); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL SYSCALL error: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); @@ -348,7 +384,7 @@ rloop: break; } - RESTORE_SIGPIPE(); + RESTORE_SIGPIPE(conn, spinfo); } else #endif @@ -364,14 +400,15 @@ ssize_t pqsecure_write(PGconn *conn, const void *ptr, size_t len) { ssize_t n; - - DISABLE_SIGPIPE(return -1); + DECLARE_SIGPIPE_INFO(spinfo); #ifdef USE_SSL if (conn->ssl) { int err; + DISABLE_SIGPIPE(conn, spinfo, return -1); + n = SSL_write(conn->ssl, ptr, len); err = SSL_get_error(conn->ssl, n); switch (err) @@ -396,7 +433,7 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len) if (n == -1) { - REMEMBER_EPIPE(SOCK_ERRNO == EPIPE); + REMEMBER_EPIPE(spinfo, SOCK_ERRNO == EPIPE); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL SYSCALL error: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); @@ -434,11 +471,41 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len) else #endif { - n = send(conn->sock, ptr, len, 0); - REMEMBER_EPIPE(n < 0 && SOCK_ERRNO == EPIPE); + int flags = 0; + +#ifdef MSG_NOSIGNAL + if (conn->sigpipe_flag) + flags |= MSG_NOSIGNAL; + +retry_masked: + +#endif /* MSG_NOSIGNAL */ + + DISABLE_SIGPIPE(conn, spinfo, return -1); + + n = send(conn->sock, ptr, len, flags); + + if (n < 0) + { + /* + * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't + * available on this machine. So, clear sigpipe_flag so we don't + * try the flag again, and retry the send(). + */ +#ifdef MSG_NOSIGNAL + if (flags != 0 && SOCK_ERRNO == EINVAL) + { + conn->sigpipe_flag = false; + flags = 0; + goto retry_masked; + } +#endif /* MSG_NOSIGNAL */ + + REMEMBER_EPIPE(spinfo, SOCK_ERRNO == EPIPE); + } } - RESTORE_SIGPIPE(); + RESTORE_SIGPIPE(conn, spinfo); return n; } @@ -1220,14 +1287,16 @@ close_SSL(PGconn *conn) { if (conn->ssl) { - DISABLE_SIGPIPE((void) 0); + DECLARE_SIGPIPE_INFO(spinfo); + + DISABLE_SIGPIPE(conn, spinfo, (void) 0); SSL_shutdown(conn->ssl); SSL_free(conn->ssl); conn->ssl = NULL; pqsecure_destroy(); /* We have to assume we got EPIPE */ - REMEMBER_EPIPE(true); - RESTORE_SIGPIPE(); + REMEMBER_EPIPE(spinfo, true); + RESTORE_SIGPIPE(conn, spinfo); } if (conn->peer) diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 51b7128b862c292bddf09c69a559ef28b13efeb8..d93ba4d25e230657863770f23f0f7351827e0293 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.143 2009/06/23 18:13:23 mha Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.144 2009/07/24 17:58:31 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -341,6 +341,8 @@ struct pg_conn ProtocolVersion pversion; /* FE/BE protocol version in use */ int sversion; /* server version, e.g. 70401 for 7.4.1 */ bool password_needed; /* true if server demanded a password */ + bool sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */ + bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */ /* Transient state needed while establishing connection */ struct addrinfo *addrlist; /* list of possible backend addresses */