Heikki Linnakangas <hlinn...@iki.fi> writes: > On 07.09.2012 10:49, Tom Lane wrote: >> I'm a bit tempted though to pull out and apply the portions of the >> patch that replace libpq's assorted ad-hoc closesocket() calls with >> a centralized pqDropConnection routine. I think that's probably a good >> idea independently of this feature.
> Sounds good. Done; here's the rebased patch. regards, tom lane
diff --git a/src/backend/main/main.c b/src/backend/main/main.c index 33c5a0a4e645624515016397da0011423d993c70..968959b85ef53aa2ec0ef8c5c5a9bc544291bfe5 100644 *** a/src/backend/main/main.c --- b/src/backend/main/main.c *************** main(int argc, char *argv[]) *** 191,196 **** --- 191,198 ---- AuxiliaryProcessMain(argc, argv); /* does not return */ else if (argc > 1 && strcmp(argv[1], "--describe-config") == 0) GucInfoMain(); /* does not return */ + else if (argc > 1 && strncmp(argv[1], "--child=", 8) == 0) + ChildPostgresMain(argc, argv, get_current_username(progname)); /* does not return */ else if (argc > 1 && strcmp(argv[1], "--single") == 0) PostgresMain(argc, argv, get_current_username(progname)); /* does not return */ else diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 73520a6ca2f4d3300f3d8939c0e9412064a911c3..5a51fa9cb9abb39b0ac02d21a25e5940159582ea 100644 *** a/src/backend/postmaster/postmaster.c --- b/src/backend/postmaster/postmaster.c *************** ExitPostmaster(int status) *** 4268,4273 **** --- 4268,4350 ---- proc_exit(status); } + + /* + * ChildPostgresMain - start a new-style standalone postgres process + * + * This may not belong here, but it does share a lot of code with ConnCreate + * and BackendInitialize. Basically what it has to do is set up a + * MyProcPort structure and then hand off control to PostgresMain. + * Beware that not very much stuff is initialized yet. + * + * In the future it might be interesting to support a "standalone + * multiprocess" mode in which we have a postmaster process that doesn't + * listen for connections, but does supervise autovacuum, bgwriter, etc + * auxiliary processes. So that's another reason why postmaster.c might be + * the right place for this. + */ + void + ChildPostgresMain(int argc, char *argv[], const char *username) + { + Port *port; + + /* + * Fire up essential subsystems: error and memory management + */ + MemoryContextInit(); + + /* + * Build a Port structure for the client connection + */ + if (!(port = (Port *) calloc(1, sizeof(Port)))) + ereport(FATAL, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + + /* + * GSSAPI specific state struct must exist even though we won't use it + */ + #if defined(ENABLE_GSS) || defined(ENABLE_SSPI) + port->gss = (pg_gssinfo *) calloc(1, sizeof(pg_gssinfo)); + if (!port->gss) + ereport(FATAL, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + #endif + + /* The file descriptor of the client socket is the argument of --child */ + if (sscanf(argv[1], "--child=%d", &port->sock) != 1) + ereport(FATAL, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid argument for --child: \"%s\"", argv[1]))); + + /* Default assumption about protocol to use */ + FrontendProtocol = port->proto = PG_PROTOCOL_LATEST; + + /* save process start time */ + port->SessionStartTime = GetCurrentTimestamp(); + MyStartTime = timestamptz_to_time_t(port->SessionStartTime); + + /* set these to empty in case they are needed */ + port->remote_host = ""; + port->remote_port = ""; + + MyProcPort = port; + + /* + * We can now initialize libpq and then enable reporting of ereport errors + * to the client. + */ + pq_init(); /* initialize libpq to talk to client */ + whereToSendOutput = DestRemote; /* now safe to ereport to client */ + + /* And pass off control to PostgresMain */ + PostgresMain(argc, argv, username); + + abort(); /* not reached */ + } + + /* * sigusr1_handler - handle signal conditions from child processes */ diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index f1248a851bf90188da8d3a7e8b61ac99bf78ebbd..c6a7de63c77f79c85be2170d4e5254b790f3fd5e 100644 *** a/src/backend/tcop/postgres.c --- b/src/backend/tcop/postgres.c *************** process_postgres_switches(int argc, char *** 3257,3264 **** { gucsource = PGC_S_ARGV; /* switches came from command line */ ! /* Ignore the initial --single argument, if present */ ! if (argc > 1 && strcmp(argv[1], "--single") == 0) { argv++; argc--; --- 3257,3266 ---- { gucsource = PGC_S_ARGV; /* switches came from command line */ ! /* Ignore the initial --single or --child argument, if present */ ! if (argc > 1 && ! (strcmp(argv[1], "--single") == 0 || ! strncmp(argv[1], "--child=", 8) == 0)) { argv++; argc--; *************** PostgresMain(int argc, char *argv[], con *** 3522,3539 **** * standalone). */ if (!IsUnderPostmaster) - { MyProcPid = getpid(); MyStartTime = time(NULL); - } /* * Fire up essential subsystems: error and memory management * ! * If we are running under the postmaster, this is done already. */ ! if (!IsUnderPostmaster) MemoryContextInit(); SetProcessingMode(InitProcessing); --- 3524,3541 ---- * standalone). */ if (!IsUnderPostmaster) MyProcPid = getpid(); + if (MyProcPort == NULL) MyStartTime = time(NULL); /* * Fire up essential subsystems: error and memory management * ! * If we are running under the postmaster, this is done already; likewise ! * in child-postgres mode. */ ! if (MyProcPort == NULL) MemoryContextInit(); SetProcessingMode(InitProcessing); diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h index 0fe7ec26db7646472fca248dee6982dc5c595c6c..6d4245cdc05ea5fea4baccf5dbc52dade7c00ff0 100644 *** a/src/include/postmaster/postmaster.h --- b/src/include/postmaster/postmaster.h *************** extern int postmaster_alive_fds[2]; *** 47,52 **** --- 47,55 ---- extern const char *progname; extern void PostmasterMain(int argc, char *argv[]) __attribute__((noreturn)); + + extern void ChildPostgresMain(int argc, char *argv[], const char *username) __attribute__((noreturn)); + extern void ClosePostmasterPorts(bool am_syslogger); extern int MaxLivePostmasterChildren(void); diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 9eaf41025beb652c6c242035490d93319a6bc5d0..c578a8ebbe6874825afbd0cb27cdfa2a3d01aec7 100644 *** a/src/interfaces/libpq/fe-connect.c --- b/src/interfaces/libpq/fe-connect.c *************** *** 17,22 **** --- 17,23 ---- #include <sys/types.h> #include <sys/stat.h> + #include <sys/wait.h> #include <fcntl.h> #include <ctype.h> #include <time.h> *************** static const PQconninfoOption PQconninfo *** 259,264 **** --- 260,271 ---- {"replication", NULL, NULL, NULL, "Replication", "D", 5}, + {"standalone_datadir", NULL, NULL, NULL, + "Standalone-Data-Directory", "D", 64}, + + {"standalone_backend", NULL, NULL, NULL, + "Standalone-Backend", "D", 64}, + /* Terminating entry --- MUST BE LAST */ {NULL, NULL, NULL, NULL, NULL, NULL, 0} *************** pqDropConnection(PGconn *conn) *** 360,365 **** --- 367,383 ---- if (conn->sock >= 0) closesocket(conn->sock); conn->sock = -1; + /* If we forked a child postgres process, wait for it to exit */ + if (conn->postgres_pid > 0) + { + while (waitpid(conn->postgres_pid, NULL, 0) < 0) + { + /* If interrupted by signal, keep waiting */ + if (errno != EINTR) + break; + } + } + conn->postgres_pid = -1; /* Discard any unread/unsent data */ conn->inStart = conn->inCursor = conn->inEnd = 0; conn->outCount = 0; *************** fillPGconn(PGconn *conn, PQconninfoOptio *** 643,648 **** --- 661,670 ---- conn->pghost = tmp ? strdup(tmp) : NULL; tmp = conninfo_getval(connOptions, "port"); conn->pgport = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "standalone_datadir"); + conn->standalone_datadir = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "standalone_backend"); + conn->standalone_backend = tmp ? strdup(tmp) : NULL; tmp = conninfo_getval(connOptions, "tty"); conn->pgtty = tmp ? strdup(tmp) : NULL; tmp = conninfo_getval(connOptions, "options"); *************** setKeepalivesWin32(PGconn *conn) *** 1311,1316 **** --- 1333,1384 ---- #endif /* SIO_KEEPALIVE_VALS */ #endif /* WIN32 */ + /* + * Fork and exec a command, connecting up a pair of anonymous sockets for + * communication. argv[fdarg] is modified by appending the child-side + * socket's file descriptor number. The parent-side socket's FD is returned + * in *psock, and the function result is the child's PID. + */ + static pid_t + fork_backend_child(char **argv, int fdarg, int *psock) + { + pid_t pid; + int socks[2]; + char newfdarg[32]; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) < 0) + return -1; + + pid = fork(); + + if (pid < 0) + { + /* fork failed */ + close(socks[0]); + close(socks[1]); + return pid; + } + else if (pid == 0) + { + /* successful, in child process */ + close(socks[1]); + snprintf(newfdarg, sizeof(newfdarg), "%s%d", argv[fdarg], socks[0]); + argv[fdarg] = newfdarg; + execv(argv[0], argv); + perror(argv[0]); + exit(1); + } + else + { + /* successful, in parent process */ + close(socks[0]); + *psock = socks[1]; + } + + return pid; + } + + /* ---------- * connectDBStart - * Begin the process of making a connection to the backend. *************** connectDBStart(PGconn *conn) *** 1339,1344 **** --- 1407,1461 ---- conn->outCount = 0; /* + * If the standalone_datadir option was specified, ignore any host or + * port specifications and just try to fork a standalone backend. + */ + if (conn->standalone_datadir && conn->standalone_datadir[0]) + { + char *be_argv[10]; + + /* + * We set up the backend's command line in execv(3) style, so that + * we don't need to cope with shell quoting rules. + */ + if (conn->standalone_backend && conn->standalone_backend[0]) + be_argv[0] = conn->standalone_backend; + else /* assume we should use hard-wired path */ + be_argv[0] = PGBINDIR "/postgres"; + + be_argv[1] = "--child="; + be_argv[2] = "-D"; + be_argv[3] = conn->standalone_datadir; + be_argv[4] = (conn->dbName && conn->dbName[0]) ? conn->dbName : NULL; + be_argv[5] = NULL; + + conn->postgres_pid = fork_backend_child(be_argv, 1, &conn->sock); + if (conn->postgres_pid < 0) + { + char sebuf[256]; + + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not fork standalone backend: %s\n"), + pqStrerror(errno, sebuf, sizeof(sebuf))); + goto connect_errReturn; + } + + /* + * Go directly to CONNECTION_AUTH_OK state, since the standalone + * backend is not going to issue any authentication challenge to us. + * We're just waiting for startup to conclude. + */ + #ifdef USE_SSL + conn->allow_ssl_try = false; + #endif + conn->pversion = PG_PROTOCOL(3, 0); + conn->status = CONNECTION_AUTH_OK; + conn->asyncStatus = PGASYNC_BUSY; + + return 1; + } + + /* * Determine the parameters to pass to pg_getaddrinfo_all. */ *************** makeEmptyPGconn(void) *** 2708,2713 **** --- 2825,2831 ---- conn->auth_req_received = false; conn->password_needed = false; conn->dot_pgpass_used = false; + conn->postgres_pid = -1; #ifdef USE_SSL conn->allow_ssl_try = true; conn->wait_ssl_try = false; *************** freePGconn(PGconn *conn) *** 2784,2789 **** --- 2902,2911 ---- free(conn->pgport); if (conn->pgunixsocket) free(conn->pgunixsocket); + if (conn->standalone_datadir) + free(conn->standalone_datadir); + if (conn->standalone_backend) + free(conn->standalone_backend); if (conn->pgtty) free(conn->pgtty); if (conn->connect_timeout) diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 4a6c8fedf2b6b7f2f72d588c9c504dcc8d26cc0f..a9070ace73cead020b8b42f2720eebc771f39e6e 100644 *** a/src/interfaces/libpq/libpq-int.h --- b/src/interfaces/libpq/libpq-int.h *************** struct pg_conn *** 303,308 **** --- 303,310 ---- char *pgunixsocket; /* the Unix-domain socket that the server is * listening on; if NULL, uses a default * constructed from pgport */ + char *standalone_datadir; /* data directory for standalone backend */ + char *standalone_backend; /* executable for standalone backend */ char *pgtty; /* tty on which the backend messages is * displayed (OBSOLETE, NOT USED) */ char *connect_timeout; /* connection timeout (numeric string) */ *************** struct pg_conn *** 373,378 **** --- 375,383 ---- bool sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */ bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */ + /* If we forked a child postgres process, its PID is kept here */ + pid_t postgres_pid; /* PID, or -1 if none */ + /* Transient state needed while establishing connection */ struct addrinfo *addrlist; /* list of possible backend addresses */ struct addrinfo *addr_cur; /* the one currently being tried */
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers