diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 4f24a6e876251d93cad77dd9e97805d5c5d56815..41f2472f7504a4ee3945ed70e592f3713e6236cf 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.339 2005/07/23 21:05:45 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.340 2005/07/30 15:17:18 momjian Exp $
 -->
 
 <chapter Id="runtime">
@@ -894,6 +894,53 @@ SET ENABLE_SEQSCAN TO OFF;
       </listitem>
      </varlistentry>
      
+     <varlistentry id="guc-tcp-keepalives-idle" xreflabel="tcp_keepalives_idle">
+      <term><varname>tcp_keepalives_idle</varname> (<type>integer</type>)</term>
+      <indexterm>
+       <primary><varname>tcp_keepalives_idle</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        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 0 uses the system default. If TCP_KEEPIDLE is
+        not supported, this parameter must be 0. This option is ignored for
+        connections made via a Unix-domain socket.
+       </para>
+      </listitem>
+     </varlistentry>
+     
+     <varlistentry id="guc-tcp-keepalives-interval" xreflabel="tcp_keepalives_interval">
+      <term><varname>tcp_keepalives_interval</varname> (<type>integer</type>)</term>
+      <indexterm>
+       <primary><varname>tcp_keepalives_interval</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        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 0 uses the system default. If TCP_KEEPINTVL
+        is not supported, this parameter must be 0. This option is ignored
+        for connections made via a Unix-domain socket.
+       </para>
+      </listitem>
+     </varlistentry>
+     
+     <varlistentry id="guc-tcp-keepalives-count" xreflabel="tcp_keepalives_count">
+      <term><varname>tcp_keepalives_count</varname> (<type>integer</type>)</term>
+      <indexterm>
+       <primary><varname>tcp_keepalives_count</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        On systems that support the TCP_KEEPCNT socket option, specifies how
+        many keepalives may be lost before the connection is considered dead. 
+        A value of 0 uses the system default. If TCP_KEEPINTVL is not
+        supported, this parameter must be 0.
+       </para>
+      </listitem>
+     </varlistentry>
+     
      </variablelist>
      </sect3>
      <sect3 id="runtime-config-connection-security">
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index ce321af4b137a69e868c32a1b0e86ae847b17413..43c2a88a451522e19cc9ca398dfbef4d133dd918 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -30,7 +30,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.176 2005/02/22 04:35:57 momjian Exp $
+ *	$PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.177 2005/07/30 15:17:20 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -87,7 +87,7 @@
 #include "libpq/libpq.h"
 #include "miscadmin.h"
 #include "storage/ipc.h"
-
+#include "utils/guc.h"
 
 /*
  * Configuration options
@@ -594,6 +594,19 @@ StreamConnection(int server_fd, Port *port)
 			elog(LOG, "setsockopt(SO_KEEPALIVE) failed: %m");
 			return STATUS_ERROR;
 		}
+
+		/* Set default keepalive parameters. This should also catch
+		 * misconfigurations (non-zero values when socket options aren't
+		 * supported)
+		 */
+		if (pq_setkeepalivesidle(tcp_keepalives_idle, port) != STATUS_OK)
+			return STATUS_ERROR;
+
+		if (pq_setkeepalivesinterval(tcp_keepalives_interval, port) != STATUS_OK)
+			return STATUS_ERROR;
+
+		if (pq_setkeepalivescount(tcp_keepalives_count, port) != STATUS_OK)
+			return STATUS_ERROR;
 	}
 
 	return STATUS_OK;
@@ -1158,3 +1171,199 @@ pq_endcopyout(bool errorAbort)
 	/* in non-error case, copy.c will have emitted the terminator line */
 	DoingCopyOut = false;
 }
+
+int
+pq_getkeepalivesidle(Port *port)
+{
+#ifdef TCP_KEEPIDLE
+	if (IS_AF_UNIX(port->laddr.addr.ss_family))
+		return 0;
+
+	if (port->keepalives_idle != 0)
+		return port->keepalives_idle;
+
+	if (port->default_keepalives_idle == 0)
+	{
+		socklen_t size = sizeof(port->default_keepalives_idle);
+		if (getsockopt(port->sock, SOL_TCP, TCP_KEEPIDLE,
+					   (char *) &port->default_keepalives_idle, 
+					   &size) < 0)
+		{
+			elog(LOG, "getsockopt(TCP_KEEPIDLE) failed: %m");
+			return -1;
+		}
+	}
+
+	return port->default_keepalives_idle;
+#else
+	return 0;
+#endif
+}
+   
+int
+pq_setkeepalivesidle(int idle, Port *port)
+{
+	if (IS_AF_UNIX(port->laddr.addr.ss_family))
+		return STATUS_OK;
+
+#ifdef TCP_KEEPIDLE
+	if (idle == port->keepalives_idle)
+		return STATUS_OK;
+
+	if (port->default_keepalives_idle == 0)
+	{
+		if (pq_getkeepalivesidle(port) < 0)
+			return STATUS_ERROR;
+	}
+			
+	if (idle == 0)
+		idle = port->default_keepalives_idle;
+
+	if (setsockopt(port->sock, SOL_TCP, TCP_KEEPIDLE,
+				   (char *) &idle, sizeof(idle)) < 0)
+	{
+		elog(LOG, "setsockopt(TCP_KEEPIDLE) failed: %m");
+		return STATUS_ERROR;
+	}
+
+	port->keepalives_idle = idle;
+#else
+	if (idle != 0)
+	{
+		elog(LOG, "setsockopt(TCP_KEEPIDLE) not supported");
+		return STATUS_ERROR;
+	}
+#endif
+
+	return STATUS_OK;
+}
+
+int
+pq_getkeepalivesinterval(Port *port)
+{
+#ifdef TCP_KEEPINTVL
+	if (IS_AF_UNIX(port->laddr.addr.ss_family))
+		return 0;
+
+	if (port->keepalives_interval != 0)
+		return port->keepalives_interval;
+
+	if (port->default_keepalives_interval == 0)
+	{
+		socklen_t size = sizeof(port->default_keepalives_interval);
+		if (getsockopt(port->sock, SOL_TCP, TCP_KEEPINTVL,
+					   (char *) &port->default_keepalives_interval, 
+					   &size) < 0)
+		{
+			elog(LOG, "getsockopt(TCP_KEEPINTVL) failed: %m");
+			return -1;
+		}
+	}
+
+	return port->default_keepalives_interval;
+#else
+	return 0;
+#endif
+}
+   
+int
+pq_setkeepalivesinterval(int interval, Port *port)
+{
+	if (IS_AF_UNIX(port->laddr.addr.ss_family))
+		return STATUS_OK;
+
+#ifdef TCP_KEEPINTVL
+	if (interval == port->keepalives_interval)
+		return STATUS_OK;
+
+	if (port->default_keepalives_interval == 0) {
+		if (pq_getkeepalivesinterval(port) < 0)
+			return STATUS_ERROR;
+	}
+			
+	if (interval == 0)
+		interval = port->default_keepalives_interval;
+
+	if (setsockopt(port->sock, SOL_TCP, TCP_KEEPINTVL,
+				   (char *) &interval, sizeof(interval)) < 0)
+	{
+		elog(LOG, "setsockopt(TCP_KEEPINTVL) failed: %m");
+		return STATUS_ERROR;
+	}
+
+	port->keepalives_interval = interval;
+#else
+	if (interval != 0)
+	{
+		elog(LOG, "setsockopt(TCP_KEEPINTVL) not supported");
+		return STATUS_ERROR;
+	}		
+#endif
+
+	return STATUS_OK;
+}
+
+int
+pq_getkeepalivescount(Port *port)
+{
+#ifdef TCP_KEEPCNT
+	if (IS_AF_UNIX(port->laddr.addr.ss_family))
+		return 0;
+
+	if (port->keepalives_count != 0)
+		return port->keepalives_count;
+
+	if (port->default_keepalives_count == 0)
+	{
+		socklen_t size = sizeof(port->default_keepalives_count);
+		if (getsockopt(port->sock, SOL_TCP, TCP_KEEPCNT,
+					   (char *) &port->default_keepalives_count, 
+					   &size) < 0)
+		{
+			elog(LOG, "getsockopt(TCP_KEEPCNT) failed: %m");
+			return -1;
+		}
+	}
+
+	return port->default_keepalives_count;
+#else
+	return 0;
+#endif
+}
+   
+int
+pq_setkeepalivescount(int count, Port *port)
+{
+	if (IS_AF_UNIX(port->laddr.addr.ss_family))
+		return STATUS_OK;
+
+#ifdef TCP_KEEPCNT
+	if (count == port->keepalives_count)
+		return STATUS_OK;
+
+	if (port->default_keepalives_count == 0) {
+		if (pq_getkeepalivescount(port) < 0)
+			return STATUS_ERROR;
+	}
+			
+	if (count == 0)
+		count = port->default_keepalives_count;
+
+	if (setsockopt(port->sock, SOL_TCP, TCP_KEEPCNT,
+				   (char *) &count, sizeof(count)) < 0)
+	{
+		elog(LOG, "setsockopt(TCP_KEEPCNT) failed: %m");
+		return STATUS_ERROR;
+	}
+
+	port->keepalives_count = count;
+#else
+	if (count != 0)
+	{
+		elog(LOG, "setsockopt(TCP_KEEPCNT) not supported");
+		return STATUS_ERROR;
+	}
+#endif
+
+	return STATUS_OK;
+}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index da6aa1a9c36d9396e8cf8ae6d64717cfa5f9f9b9..f3426b18cab0502493107c9a0025649305b352f0 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.279 2005/07/29 19:30:07 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.280 2005/07/30 15:17:20 momjian Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -120,6 +120,12 @@ static bool assign_log_stats(bool newval, bool doit, GucSource source);
 static bool assign_transaction_read_only(bool newval, bool doit, GucSource source);
 static const char *assign_canonical_path(const char *newval, bool doit, GucSource source);
 
+static bool assign_tcp_keepalives_idle(int newval, bool doit, GucSource source);
+static bool assign_tcp_keepalives_interval(int newval, bool doit, GucSource source);
+static bool assign_tcp_keepalives_count(int newval, bool doit, GucSource source);
+static const char *show_tcp_keepalives_idle(void);
+static const char *show_tcp_keepalives_interval(void);
+static const char *show_tcp_keepalives_count(void);
 
 /*
  * GUC option variables that are exported from this module
@@ -161,6 +167,9 @@ char	   *HbaFileName;
 char	   *IdentFileName;
 char	   *external_pid_file;
 
+int         tcp_keepalives_idle;
+int         tcp_keepalives_interval;
+int         tcp_keepalives_count;
 
 /*
  * These variables are all dummies that don't do anything, except in some
@@ -1437,6 +1446,35 @@ static struct config_int ConfigureNamesInt[] =
 		500, 0, INT_MAX, NULL, NULL
 	},
 
+	{
+		{"tcp_keepalives_idle", PGC_USERSET, CLIENT_CONN_OTHER,
+		     gettext_noop("Seconds between issuing TCP keepalives."),
+		     gettext_noop("A value of 0 uses the system default."),
+		},		
+		&tcp_keepalives_idle,
+		0, 0, INT_MAX, assign_tcp_keepalives_idle, show_tcp_keepalives_idle
+	},
+
+	{
+		{"tcp_keepalives_interval", PGC_USERSET, CLIENT_CONN_OTHER,
+		     gettext_noop("Seconds between TCP keepalive retransmits."),
+		     gettext_noop("A value of 0 uses the system default."),
+		},		
+		&tcp_keepalives_interval,
+		0, 0, INT_MAX, assign_tcp_keepalives_interval, show_tcp_keepalives_interval
+	},
+
+	{
+		{"tcp_keepalives_count", PGC_USERSET, CLIENT_CONN_OTHER,
+		     gettext_noop("Maximum number of TCP keepalive retransmits."),
+		     gettext_noop("This controls the number of consecutive keepalive retransmits that can be "
+						  "lost before a connection is considered dead. A value of 0 uses the "
+						  "system default."),
+		},		
+		&tcp_keepalives_count,
+		0, 0, INT_MAX, assign_tcp_keepalives_count, show_tcp_keepalives_count
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL
@@ -5815,5 +5853,61 @@ assign_canonical_path(const char *newval, bool doit, GucSource source)
 		return newval;
 }
 
+static bool
+assign_tcp_keepalives_idle(int newval, bool doit, GucSource source)
+{
+	if (doit && MyProcPort != NULL)
+	{
+		return (pq_setkeepalivesidle(newval, MyProcPort) == STATUS_OK);
+	}
+
+	return true;
+}
+
+static const char *
+show_tcp_keepalives_idle(void)
+{
+	static char nbuf[32];
+	snprintf(nbuf, sizeof(nbuf), "%d", MyProcPort == NULL ? 0 : pq_getkeepalivesidle(MyProcPort));
+	return nbuf;
+}
+
+static bool
+assign_tcp_keepalives_interval(int newval, bool doit, GucSource source)
+{
+	if (doit && MyProcPort != NULL)
+	{
+		return (pq_setkeepalivesinterval(newval, MyProcPort) == STATUS_OK);
+	}
+
+	return true;
+}
+
+static const char *
+show_tcp_keepalives_interval(void)
+{
+	static char nbuf[32];
+	snprintf(nbuf, sizeof(nbuf), "%d", MyProcPort == NULL ? 0 : pq_getkeepalivesinterval(MyProcPort));
+	return nbuf;
+}
+
+static bool
+assign_tcp_keepalives_count(int newval, bool doit, GucSource source)
+{
+	if (doit && MyProcPort != NULL)
+	{
+		return (pq_setkeepalivescount(newval, MyProcPort) == STATUS_OK);
+	}
+
+	return true;
+}
+
+static const char *
+show_tcp_keepalives_count(void)
+{
+	static char nbuf[32];
+	snprintf(nbuf, sizeof(nbuf), "%d", MyProcPort == NULL ? 0 : pq_getkeepalivescount(MyProcPort));
+	return nbuf;
+}
 
 #include "guc-file.c"
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index db8c28814db8295064c714e940717ad8adc9f369..5eabe09552ec92b10274784781de90f74c4a7668 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -70,6 +70,11 @@
 #krb_caseins_users = off
 #krb_srvname = 'postgres'
 
+# - TCP Keepalives -
+# see 'man 7 tcp' for details
+#tcp_keepalives_idle = 0        # TCP_KEEPIDLE, in seconds; 0 uses the system default.
+#tcp_keepalives_interval = 0    # TCP_KEEPINTVL, in seconds; 0 uses the system default.
+#tcp_keepalives_count = 0       # TCP_KEEPCNT, in seconds; 0 uses the system default.
 
 #---------------------------------------------------------------------------
 # RESOURCE USAGE (except WAL)
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 80441733fb1fcf64e01afe6d2c4e5345cea291e5..2be131993bb884910c0c643f652a8725a6634507 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.135 2005/07/28 22:14:30 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.136 2005/07/30 15:17:22 momjian Exp $
  */
 
 /*----------------------------------------------------------------------
@@ -601,6 +601,9 @@ psql_completion(char *text, int start, int end)
 		"superuser_reserved_connections",
 		"syslog_facility",
 		"syslog_ident",
+		"tcp_keepalives_idle",
+		"tcp_keepalives_interval",
+		"tcp_keepalives_count",
 		"temp_buffers",
 		"TimeZone",
 		"trace_notify",
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index cec679577bd880d56f126572e2bdae6a396764ac..bbc218a0e1e5fdf1b25cb1ed1166e7eaeb84cf56 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/libpq/libpq-be.h,v 1.49 2004/12/31 22:03:32 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/libpq-be.h,v 1.50 2005/07/30 15:17:25 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,6 +25,9 @@
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
 
 #include "libpq/hba.h"
 #include "libpq/pqcomm.h"
@@ -92,9 +95,37 @@ typedef struct Port
 	char		peer_cn[SM_USER + 1];
 	unsigned long count;
 #endif
+
+	/*
+	 * TCP keepalive settings;
+	 *  default values are 0 if AF_UNIX or not yet known;
+	 *  current values are 0 if AF_UNIX or using the default.
+	 */
+#ifdef TCP_KEEPIDLE
+	int         default_keepalives_idle;
+	int         keepalives_idle;
+#endif
+#ifdef TCP_KEEPINTVL
+	int         default_keepalives_interval;
+	int         keepalives_interval;
+#endif
+#ifdef TCP_KEEPCNT
+	int         default_keepalives_count;
+	int         keepalives_count;
+#endif
 } Port;
 
 
 extern ProtocolVersion FrontendProtocol;
 
+/* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
+
+extern int pq_getkeepalivesidle(Port *port);
+extern int pq_getkeepalivesinterval(Port *port);
+extern int pq_getkeepalivescount(Port *port);
+
+extern int pq_setkeepalivesidle(int idle, Port *port);
+extern int pq_setkeepalivesinterval(int interval, Port *port);
+extern int pq_setkeepalivescount(int count, Port *port);
+
 #endif   /* LIBPQ_BE_H */
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index c08ecd4db4245d2a15b4ff3e8c0a850f917c2cee..00399bd488caf5f0cb0597334681ad878e12c619 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -7,7 +7,7 @@
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
- * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.61 2005/06/26 03:04:12 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.62 2005/07/30 15:17:26 momjian Exp $
  *--------------------------------------------------------------------
  */
 #ifndef GUC_H
@@ -134,6 +134,9 @@ extern char *HbaFileName;
 extern char *IdentFileName;
 extern char *external_pid_file;
 
+extern int  tcp_keepalives_idle;
+extern int  tcp_keepalives_interval;
+extern int  tcp_keepalives_count;
 
 extern void SetConfigOption(const char *name, const char *value,
 				GucContext context, GucSource source);