diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index d3a2218182599335948f7a59d6ce53277f86321a..11161d993f5923f0c5c87d7ff447ad152b95c384 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.308 2010/06/17 16:03:30 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.309 2010/06/23 21:54:13 rhaas Exp $ --> <chapter id="libpq"> <title><application>libpq</application> - C Library</title> @@ -280,6 +280,57 @@ </listitem> </varlistentry> + <varlistentry id="libpq-keepalives" xreflabel="keepalives"> + <term><literal>keepalives</literal></term> + <listitem> + <para> + 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. + </para> + </listitem> + </varlistentry> + + <varlistentry id="libpq-keepalives-idle" xreflabel="keepalives_idle"> + <term><literal>keepalives_idle</literal></term> + <listitem> + <para> + On systems that support the <symbol>TCP_KEEPIDLE</symbol> 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. + </para> + </listitem> + </varlistentry> + + <varlistentry id="libpq-keepalives-interval" xreflabel="keepalives_interval"> + <term><literal>keepalives_interval</literal></term> + <listitem> + <para> + On systems that support the <symbol>TCP_KEEPINTVL</symbol> 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. + </para> + </listitem> + </varlistentry> + + <varlistentry id="libpq-keepalives-count" xreflabel="keepalives_count"> + <term><literal>keepalives_count</literal></term> + <listitem> + <para> + On systems that support the <symbol>TCP_KEEPCNT</symbol> 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. + </para> + </listitem> + </varlistentry> + <varlistentry id="libpq-connect-tty" xreflabel="tty"> <term><literal>tty</literal></term> <listitem> diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 3a1465457e08f2e3c7c326a1ed7f0f8d3ddf8bc4..795b64e55bf64933dfca6e773657ebd938731558 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.393 2010/05/26 21:39:27 tgl Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.394 2010/06/23 21:54:13 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -183,6 +183,18 @@ static const PQconninfoOption PQconninfoOptions[] = { {"fallback_application_name", NULL, NULL, NULL, "Fallback-Application-Name", "", 64}, + {"keepalives", NULL, NULL, NULL, + "TCP-Keepalives", "", 1}, /* should be just '0' or '1' */ + + {"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 6fe96ab1684bab7832d3c67bb57ec8b911f8165b..23b914abbc57082cfeb351772d403911a9485d9a 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.150 2010/03/13 14:55:57 momjian Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.151 2010/06/23 21:54:13 rhaas Exp $ * *------------------------------------------------------------------------- */ @@ -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 */