Hello! We are seeing connection failures when using "sslmode=require" on forked connections. Attached is example code that makes 2 passes. The first pass uses "sslmode=disable" and the second uses "sslmode=require". The first pass completes successfully, but the second pass fails. I'm looking for insight as to why this might be happening.
Note: we are very aware of the dev notes about forking, however know that we are not sharing the forked connection, we simply open the connection in the parent thread and then pass that to the child thread to use. Thank you for any insight, -Jim P.
/* gcc -I/usr/include/postgresql -L/usr/lib/postgresql/12/lib -o pq-test pq-test.c -lpq */ #include <libpq-fe.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <errno.h> #include <unistd.h> #include <time.h> #include <sys/wait.h> #include <sys/types.h> /* Sample output: ** pottmi@ubuntu ~/src ** % ./pq-test ** [37095] with sslmode=require: ** [37095] Calling New() ** [37097] Calling Exec() ** [37098] Calling Exec() ** [37098] Error in results: PGRES_FATAL_ERROR ** ** [37095] with sslmode=disable: ** [37095] Calling New() ** [37100] Calling Exec() ** [37101] Calling Exec() ** pottmi@ubuntu ~/src ** ** pottmi@ubuntu ~/src ** % uname -a ** Linux ubuntu 5.13.0-40-generic #45~20.04.1-Ubuntu SMP Mon Apr 4 09:38:31 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux ** pottmi@ubuntu ~/src ** % cat /etc/debian_version ** bullseye/sid ** pottmi@ubuntu ~/src */ char *connectwillfail = "postgresql://kicktest:changeme@localhost:5432/kicktest?sslmode=require"; char *connectwillsucceed = "postgresql://kicktest:changeme@localhost:5432/kicktest?sslmode=disable"; char *sql = "select count(*) from information_schema.tables where table_schema = 'public'"; PGconn *New(const char *connectstr) { PGconn *con; printf("[%d] Calling New()\n", getpid()); con = PQconnectdb(connectstr); if (PQstatus(con) != CONNECTION_OK) { printf("[%d] could not connect to %s because %s\n", getpid(), connectstr, PQerrorMessage(con)); } return(con); } void Exec(PGconn *con, char *sql) { printf("[%d] Calling Exec()\n", getpid()); PGresult *result; ExecStatusType status; result = PQexec(con, sql); status = PQresultStatus(result); if (status != PGRES_TUPLES_OK) { printf("[%d] Error in results: %s\n", getpid(), PQresStatus(status)); } PQclear(result); } void runit(const char *connectstring) { int status; // Allocate connection in parent, but we will not use it in the parent process. PGconn *con = New(connectstring); int pid = fork(); if (pid == 0) // Child process { // use connection in first child. Exec(con, sql); exit(0); } else if (pid > 0) // Parent process { int pidrtn = wait(&status); } else // if (pid < 0) // Error { printf("[%d] Failed to fork! (errno=%d)\n", pid, errno); exit(1); } pid = fork(); if (pid == 0) // Child process { // use connection in a second child. Exec(con, sql); exit(0); } else if (pid > 0) // Parent process { int pidrtn = wait(&status); } else // if (pid < 0) // Error { printf("[%d] Failed to fork! (errno=%d)\n", pid, errno); exit(1); } PQfinish(con); } void main(void) { printf("[%d] with sslmode=require:\n", getpid()); runit(connectwillfail); printf("\n[%d] with sslmode=disable:\n", getpid()); runit(connectwillsucceed); }