diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 8448bc4..9a3e1ca 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -280,6 +280,57 @@
+
+ keepalives
+
+
+ Controls whether TCP keepalives are used. The default value is 1,
+ meaning on, but you can change this to 0, meaning off, if keepalives
+ are not wanted. This parameter is ignored for connections made via
+ a Unix-domain socket.
+
+
+
+
+
+ keepalives_idle
+
+
+ On systems that support the TCP_KEEPIDLE socket
+ option, specifies the number of seconds between sending keepalives
+ on an otherwise idle connection. A value of zero uses the system
+ default. This parameter is ignored for connections made via a
+ Unix-domain socket, or if keepalives are disabled.
+
+
+
+
+
+ keepalives_interval
+
+
+ On systems that support the TCP_KEEPINTVL socket
+ option, specifies how long, in seconds, to wait for a response to a
+ keepalive before retransmitting. A value of zero uses the system
+ default. This parameter is ignored for connections made via a
+ Unix-domain socket, or if keepalives are disabled.
+
+
+
+
+
+ keepalives_count
+
+
+ On systems that support the TCP_KEEPCNT socket
+ option, specifies how many keepalives can be lost before the
+ connection is considered dead. A value of zero uses the system
+ default. This parameter is ignored for connections made via a
+ Unix-domain socket, or if keepalives are disabled.
+
+
+
+
tty
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index ab6cc08..8240404 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -183,6 +183,18 @@ static const PQconninfoOption PQconninfoOptions[] = {
{"fallback_application_name", NULL, NULL, NULL,
"Fallback-Application-Name", "", 64},
+ {"keepalives", NULL, NULL, NULL,
+ "TCP-Keepalives", "", 10}, /* strlen(INT32_MAX) == 10 */
+
+ {"keepalives_idle", NULL, NULL, NULL,
+ "TCP-Keepalives-Idle", "", 10}, /* strlen(INT32_MAX) == 10 */
+
+ {"keepalives_interval", NULL, NULL, NULL,
+ "TCP-Keepalives-Interval", "", 10}, /* strlen(INT32_MAX) == 10 */
+
+ {"keepalives_count", NULL, NULL, NULL,
+ "TCP-Keepalives-Count", "", 10}, /* strlen(INT32_MAX) == 10 */
+
#ifdef USE_SSL
/*
@@ -552,6 +564,14 @@ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
conn->pgpass = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "connect_timeout");
conn->connect_timeout = tmp ? strdup(tmp) : NULL;
+ tmp = conninfo_getval(connOptions, "keepalives");
+ conn->keepalives = tmp ? strdup(tmp) : NULL;
+ tmp = conninfo_getval(connOptions, "keepalives_idle");
+ conn->keepalives_idle = tmp ? strdup(tmp) : NULL;
+ tmp = conninfo_getval(connOptions, "keepalives_interval");
+ conn->keepalives_interval = tmp ? strdup(tmp) : NULL;
+ tmp = conninfo_getval(connOptions, "keepalives_count");
+ conn->keepalives_count = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "sslmode");
conn->sslmode = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "sslkey");
@@ -943,6 +963,119 @@ connectFailureMessage(PGconn *conn, int errorno)
}
}
+/*
+ * Should we use keepalives? Returns 1 if yes, 0 if no, and -1 if
+ * conn->keepalives is set to a value which is not parseable as an
+ * integer.
+ */
+static int
+useKeepalives(PGconn *conn)
+{
+ char *ep;
+ int val;
+
+ if (conn->keepalives == NULL)
+ return 1;
+ val = strtol(conn->keepalives, &ep, 10);
+ if (*ep)
+ return -1;
+ return val != 0 ? 1 : 0;
+}
+
+/*
+ * Set the keepalive idle timer.
+ */
+static int
+setKeepalivesIdle(PGconn *conn)
+{
+ int idle;
+
+ if (conn->keepalives_idle == NULL)
+ return 1;
+
+ idle = atoi(conn->keepalives_idle);
+ if (idle < 0)
+ idle = 0;
+
+#ifdef TCP_KEEPIDLE
+ if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPIDLE,
+ (char *) &idle, sizeof(idle)) < 0)
+ {
+ char sebuf[256];
+
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("setsockopt(TCP_KEEPIDLE) failed: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
+/*
+ * Set the keepalive interval.
+ */
+static int
+setKeepalivesInterval(PGconn *conn)
+{
+ int interval;
+
+ if (conn->keepalives_interval == NULL)
+ return 1;
+
+ interval = atoi(conn->keepalives_interval);
+ if (interval < 0)
+ interval = 0;
+
+#ifdef TCP_KEEPINTVL
+ if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPINTVL,
+ (char *) &interval, sizeof(interval)) < 0)
+ {
+ char sebuf[256];
+
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("setsockopt(TCP_KEEPINTVL) failed: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
+/*
+ * Set the count of lost keepalive packets that will trigger a connection
+ * break.
+ */
+static int
+setKeepalivesCount(PGconn *conn)
+{
+ int count;
+
+ if (conn->keepalives_count == NULL)
+ return 1;
+
+ count = atoi(conn->keepalives_count);
+ if (count < 0)
+ count = 0;
+
+#ifdef TCP_KEEPCNT
+ if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPCNT,
+ (char *) &count, sizeof(count)) < 0)
+ {
+ char sebuf[256];
+
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("setsockopt(TCP_KEEPCNT) failed: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
/* ----------
* connectDBStart -
@@ -1329,6 +1462,45 @@ keep_going: /* We will come back to here until there is
}
#endif /* F_SETFD */
+ if (!IS_AF_UNIX(addr_cur->ai_family))
+ {
+ int on = 1;
+ int usekeepalives = useKeepalives(conn);
+ int err = 0;
+
+ if (usekeepalives < 0)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("keepalives parameter must be an integer\n"));
+ err = 1;
+ }
+ else if (usekeepalives == 0)
+ {
+ /* Do nothing */
+ }
+ else if (setsockopt(conn->sock,
+ SOL_SOCKET, SO_KEEPALIVE,
+ (char *) &on, sizeof(on)) < 0)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("setsockopt(SO_KEEPALIVE) failed: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ err = 1;
+ }
+ else if (!setKeepalivesIdle(conn)
+ || !setKeepalivesInterval(conn)
+ || !setKeepalivesCount(conn))
+ err = 1;
+
+ if (err)
+ {
+ closesocket(conn->sock);
+ conn->sock = -1;
+ conn->addr_cur = addr_cur->ai_next;
+ continue;
+ }
+ }
+
/*----------
* We have three methods of blocking SIGPIPE during
* send() calls to this socket:
@@ -2290,6 +2462,14 @@ freePGconn(PGconn *conn)
free(conn->pguser);
if (conn->pgpass)
free(conn->pgpass);
+ if (conn->keepalives)
+ free(conn->keepalives);
+ if (conn->keepalives_idle)
+ free(conn->keepalives_idle);
+ if (conn->keepalives_interval)
+ free(conn->keepalives_interval);
+ if (conn->keepalives_count)
+ free(conn->keepalives_count);
if (conn->sslmode)
free(conn->sslmode);
if (conn->sslcert)
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index e88b3ec..23d58ff 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -300,6 +300,10 @@ struct pg_conn
char *replication; /* connect as the replication standby? */
char *pguser; /* Postgres username and password, if any */
char *pgpass;
+ char *keepalives; /* use TCP keepalives? */
+ char *keepalives_idle; /* time between TCP keepalives */
+ char *keepalives_interval; /* time between TCP keepalive retransmits */
+ char *keepalives_count; /* maximum number of TCP keepalive retransmits */
char *sslmode; /* SSL mode (require,prefer,allow,disable) */
char *sslkey; /* client key filename */
char *sslcert; /* client certificate filename */