*** a/src/interfaces/libpq/fe-connect.c --- b/src/interfaces/libpq/fe-connect.c *************** *** 15,22 **** --- 15,24 ---- #include "postgres_fe.h" + #include #include #include + #include #include #include #include *************** *** 379,384 **** static char *PasswordFromFile(char *hostname, char *port, char *dbname, --- 381,387 ---- static bool getPgPassFilename(char *pgpassfile); static void dot_pg_pass_warning(PGconn *conn); static void default_threadlock(int acquire); + static char *executeResolve(const char *conninfo, char *resolveCmd); /* global variable because fe-auth.c needs to access it */ *************** *** 518,523 **** PQconnectdb(const char *conninfo) --- 521,674 ---- return conn; } + static char * + executeResolve(const char *conninfo, char *resolveCmd) + { + int saved_errno; + pid_t forkStatus = -1; + + size_t conninfoLen = strlen(conninfo); + + int resolveInputPipe[2]; + int resolveOutputPipe[2]; + + char *buf = NULL; + size_t bufCap; + size_t bufLen; + + size_t readStatus; + size_t remain; + + /* Save errno for restoration */ + saved_errno = errno; + errno = 0; + + /* + * Create input/output pipes to get full duplex communication with + * the resolve commmand. + */ + if (pipe(resolveInputPipe)) + goto err; + + if (pipe(resolveOutputPipe)) + goto err; + + forkStatus = fork(); + + switch (forkStatus) + { + case 0: + /* + * Child process: shuffle file descriptors around and close + * irrelevant parts of the pipe. + */ + close(resolveInputPipe[1]); + dup2(resolveInputPipe[0], 0); + + close(resolveOutputPipe[0]); + dup2(resolveOutputPipe[1], 1); + + execl("/bin/sh", "sh", "-c", resolveCmd, NULL); + + /* If exec didn't work, just get out of the child */ + exit(1); + break; + + case -1: + /* Fork is no good: complain */ + goto err; + + default: + /* + * Parent process + * + * Close off the child-owned parts of the pipe. + */ + close(resolveInputPipe[0]); + close(resolveOutputPipe[1]); + } + + /* + * Write the entire conninfo passed by the user, or complain and + * die. + * + //Note that this can deadlock for very large conninfo, but thanks + //torelatively generous PIPE_BUF constants as seen on most + //platforms, this is okay for a prototype. + * + */ + do + { + errno = 0; + write(resolveInputPipe[1], conninfo, conninfoLen); + } while (errno == EINTR); + + if (errno) + goto err; + + /* + * Close the input part of the pipe: recovery commands rely on + * this to know when the input is finished. + */ + do + { + errno = 0; + + close(resolveInputPipe[1]); + } while (errno == EINTR); + + if (errno) + goto err; + + /* Buffer the output of the command for return. */ + buf = malloc(PIPE_BUF); + bufCap = PIPE_BUF; + bufLen = 0; + + do + { + remain = bufCap - bufLen; + + /* Allocate slack if short on remaining space. */ + if (remain < PIPE_BUF) + { + buf = realloc(buf, bufCap + PIPE_BUF); + remain += PIPE_BUF; + } + + /* Read from the pipe, tracking progress in 'buf', or die */ + do + { + errno = 0; + + readStatus = read(resolveOutputPipe[0], buf + bufLen, remain); + + bufLen += readStatus; + } while (errno == EINTR); + + if (errno) + goto err; + + } while (readStatus == remain); /* EOF condition */ + + buf[bufLen] = '\0'; + + return buf; + + err: + fprintf(stderr, "dies: %d\n", errno); + + if (forkStatus > 0) + waitpid(forkStatus, NULL, 0); + + if (buf != NULL) + free(buf); + + errno = saved_errno; + + return NULL; + } + /* * PQping * *************** *** 4289,4294 **** conninfo_array_parse(const char *const * keywords, const char *const * values, --- 4440,4450 ---- /* first find "dbname" if any */ if (strcmp(pname, "dbname") == 0 && pvalue) { + char *resolved = NULL; + char *resolveCmd; + + resolveCmd = getenv("PGRESOLVE"); + /* * If value is a connection string, parse it, but do not use * defaults here -- those get picked up later. We only want to *************** *** 4296,4305 **** conninfo_array_parse(const char *const * keywords, const char *const * values, */ if (recognized_connection_string(pvalue)) { ! dbname_options = parse_connection_string(pvalue, errorMessage, false); if (dbname_options == NULL) return NULL; } break; } ++i; --- 4452,4485 ---- */ if (recognized_connection_string(pvalue)) { ! /* Run nested resolver*/ ! if (resolveCmd != NULL) ! resolved = executeResolve(pvalue, resolveCmd); ! ! if (resolved == NULL) ! resolved = (char *) pvalue; ! ! dbname_options = parse_connection_string(resolved, errorMessage, ! false); if (dbname_options == NULL) return NULL; } + else + { + if (resolveCmd != NULL) + { + resolved = executeResolve(pvalue, resolveCmd); + + if (resolved == NULL) + return NULL; + + dbname_options = parse_connection_string( + resolved, errorMessage, false); + + if (dbname_options == NULL) + return NULL; + } + } break; } ++i;