*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
PGresult *res;
char sebuf[256];
+ int optval;
if (conn == NULL)
return PGRES_POLLING_FAILED;
}
#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.
case CONNECTION_STARTED:
{
- int optval;
ACCEPT_TYPE_ARG3 optlen = sizeof(optval);
/*
*
*
* 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
*
/*
* 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 */
/* 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))
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);
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)));
break;
}
- RESTORE_SIGPIPE();
+ RESTORE_SIGPIPE(conn, spinfo);
}
else
#endif
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)
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)));
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;
}
{
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)