diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index d67212b831..f5f2668f15 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -7231,6 +7231,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
+
+
+
+ PGPASSCOMMAND
+
+ PGPASSCOMMAND behaves the same as the connection parameter.
+
+
+
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index a7e969d7c1..60382bc67b 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -204,6 +204,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"Database-Password-File", "", 64,
offsetof(struct pg_conn, pgpassfile)},
+ {"passcommand", "PGPASSCOMMAND", NULL, NULL,
+ "Database-Password-Command", "", 64,
+ offsetof(struct pg_conn, pgpasscommand)},
+
{"connect_timeout", "PGCONNECT_TIMEOUT", NULL, NULL,
"Connect-timeout", "", 10, /* strlen(INT32_MAX) == 10 */
offsetof(struct pg_conn, connect_timeout)},
@@ -406,8 +410,8 @@ static int parseServiceFile(const char *serviceFile,
PQExpBuffer errorMessage,
bool *group_found);
static char *pwdfMatchesString(char *buf, const char *token);
-static char *passwordFromFile(const char *hostname, const char *port, const char *dbname,
- const char *username, const char *pgpassfile);
+static char *passwordFromFileOrCommand(const char *hostname, const char *port, const char *dbname,
+ const char *username, const char *pgpassfile, const char *pgpasscommand);
static void pgpassfileWarning(PGconn *conn);
static void default_threadlock(int acquire);
@@ -1068,7 +1072,8 @@ connectOptions2(PGconn *conn)
* Supply default password if none given. Note that the password might be
* different for each host/port pair.
*/
- if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
+ if ( (conn->pgpass == NULL || conn->pgpass[0] == '\0')
+ || (conn->pgpasscommand != NULL && conn->pgpasscommand[0] != '\0') )
{
/* If password file wasn't specified, use ~/PGPASSFILE */
if (conn->pgpassfile == NULL || conn->pgpassfile[0] == '\0')
@@ -1087,7 +1092,8 @@ connectOptions2(PGconn *conn)
}
}
- if (conn->pgpassfile != NULL && conn->pgpassfile[0] != '\0')
+ if ( (conn->pgpassfile != NULL && conn->pgpassfile[0] != '\0')
+ || (conn->pgpasscommand != NULL && conn->pgpasscommand[0] != '\0') )
{
int i;
@@ -1106,11 +1112,12 @@ connectOptions2(PGconn *conn)
pwhost = conn->connhost[i].hostaddr;
conn->connhost[i].password =
- passwordFromFile(pwhost,
+ passwordFromFileOrCommand(pwhost,
conn->connhost[i].port,
conn->dbName,
conn->pguser,
- conn->pgpassfile);
+ conn->pgpassfile,
+ conn->pgpasscommand);
/* If we got one, set pgpassfile_used */
if (conn->connhost[i].password != NULL)
conn->pgpassfile_used = true;
@@ -3468,6 +3475,8 @@ freePGconn(PGconn *conn)
free(conn->pgpass);
if (conn->pgpassfile)
free(conn->pgpassfile);
+ if (conn->pgpasscommand)
+ free(conn->pgpasscommand);
if (conn->keepalives)
free(conn->keepalives);
if (conn->keepalives_idle)
@@ -6376,11 +6385,12 @@ pwdfMatchesString(char *buf, const char *token)
/* Get a password from the password file. Return value is malloc'd. */
static char *
-passwordFromFile(const char *hostname, const char *port, const char *dbname,
- const char *username, const char *pgpassfile)
+passwordFromFileOrCommand(const char *hostname, const char *port, const char *dbname,
+ const char *username, const char *pgpassfile, const char *pgpasscommand)
{
FILE *fp;
struct stat stat_buf;
+ char has_pgpassfile=0;
#define LINELEN NAMEDATALEN*5
char buf[LINELEN];
@@ -6407,11 +6417,12 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
port = DEF_PGPORT_STR;
/* If password file cannot be opened, ignore it. */
- if (stat(pgpassfile, &stat_buf) != 0)
- return NULL;
+ if (stat(pgpassfile, &stat_buf) == 0)
+ has_pgpassfile=1;
+
#ifndef WIN32
- if (!S_ISREG(stat_buf.st_mode))
+ if (has_pgpassfile && !S_ISREG(stat_buf.st_mode))
{
fprintf(stderr,
libpq_gettext("WARNING: password file \"%s\" is not a plain file\n"),
@@ -6420,7 +6431,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
}
/* If password file is insecure, alert the user and ignore it. */
- if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
+ if (has_pgpassfile && stat_buf.st_mode & (S_IRWXG | S_IRWXO))
{
fprintf(stderr,
libpq_gettext("WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
@@ -6434,8 +6445,22 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
* file.
*/
#endif
+ if (has_pgpassfile)
+ {
+ fp = fopen(pgpassfile, "r");
+ }
+ else if (pgpasscommand != NULL && pgpasscommand[0] != '\0')
+ {
+ fp = popen(pgpasscommand, "r");
+ }
+ else
+ {
+ fprintf(stderr,
+ libpq_gettext("WARNING: password file and password command are not provided, please correct code\n")
+ );
+ return NULL;
+ }
- fp = fopen(pgpassfile, "r");
if (fp == NULL)
return NULL;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 9a586ff25a..77af2ab550 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -343,6 +343,7 @@ struct pg_conn
char *pguser; /* Postgres username and password, if any */
char *pgpass;
char *pgpassfile; /* path to a file containing password(s) */
+ char *pgpasscommand; /* path to command producing the password(s) */
char *keepalives; /* use TCP keepalives? */
char *keepalives_idle; /* time between TCP keepalives */
char *keepalives_interval; /* time between TCP keepalive