diff options
Diffstat (limited to 'src/backend/postmaster/postmaster.c')
-rw-r--r-- | src/backend/postmaster/postmaster.c | 1949 |
1 files changed, 1026 insertions, 923 deletions
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 9c13eae1957..c9979b76969 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -1,45 +1,45 @@ /*------------------------------------------------------------------------- * * postmaster.c-- - * This program acts as a clearing house for requests to the - * POSTGRES system. Frontend programs send a startup message - * to the Postmaster and the postmaster uses the info in the - * message to setup a backend process. + * This program acts as a clearing house for requests to the + * POSTGRES system. Frontend programs send a startup message + * to the Postmaster and the postmaster uses the info in the + * message to setup a backend process. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.53 1997/08/25 04:15:31 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.54 1997/09/07 04:47:43 momjian Exp $ * * NOTES * * Initialization: - * The Postmaster sets up a few shared memory data structures - * for the backends. It should at the very least initialize the - * lock manager. + * The Postmaster sets up a few shared memory data structures + * for the backends. It should at the very least initialize the + * lock manager. * * Synchronization: - * The Postmaster shares memory with the backends and will have to lock - * the shared memory it accesses. The Postmaster should never block - * on messages from clients. - * + * The Postmaster shares memory with the backends and will have to lock + * the shared memory it accesses. The Postmaster should never block + * on messages from clients. + * * Garbage Collection: - * The Postmaster cleans up after backends if they have an emergency - * exit and/or core dump. + * The Postmaster cleans up after backends if they have an emergency + * exit and/or core dump. * * Communication: * *------------------------------------------------------------------------- */ - /* moved here to prevent double define */ -#include <sys/param.h> /* for MAXHOSTNAMELEN on most */ + /* moved here to prevent double define */ +#include <sys/param.h> /* for MAXHOSTNAMELEN on most */ #ifdef HAVE_NETDB_H -# include <netdb.h> /* for MAXHOSTNAMELEN on some */ +#include <netdb.h> /* for MAXHOSTNAMELEN on some */ #endif #ifndef MAXHOSTNAMELEN -# define MAXHOSTNAMELEN 256 +#define MAXHOSTNAMELEN 256 #endif #include "postgres.h" @@ -49,20 +49,20 @@ #include <stdlib.h> #if !defined(NO_UNISTD_H) -# include <unistd.h> -#endif /* !NO_UNISTD_H */ +#include <unistd.h> +#endif /* !NO_UNISTD_H */ #include <ctype.h> -#include <sys/types.h> /* for fd_set stuff */ -#include <sys/stat.h> /* for umask */ +#include <sys/types.h> /* for fd_set stuff */ +#include <sys/stat.h> /* for umask */ #include <sys/time.h> #include <sys/socket.h> #ifdef HAVE_LIMITS_H -# include <limits.h> -# define MAXINT INT_MAX +#include <limits.h> +#define MAXINT INT_MAX #else -# include <values.h> -#endif +#include <values.h> +#endif #include <sys/wait.h> #include <errno.h> @@ -70,8 +70,8 @@ #include <stdio.h> #ifdef HAVE_SYS_SELECT_H -# include <sys/select.h> -#endif +#include <sys/select.h> +#endif #include "storage/ipc.h" #include "libpq/libpq.h" @@ -85,137 +85,152 @@ #include "utils/mcxt.h" #include "storage/proc.h" #include "utils/elog.h" -#include "port-protos.h" /* For gethostname() */ +#include "port-protos.h" /* For gethostname() */ #if defined(DBX_VERSION) -# define FORK() (0) +#define FORK() (0) #else -# ifndef HAVE_VFORK -# define FORK() fork() -# else -# define FORK() vfork() -# endif +#ifndef HAVE_VFORK +#define FORK() fork() +#else +#define FORK() vfork() +#endif #endif #define LINGER_TIME 3 - /* Max time in seconds for socket to linger (close() to block) waiting - for frontend to retrieve its message from us. + + /* + * Max time in seconds for socket to linger (close() to block) waiting + * for frontend to retrieve its message from us. */ /* * Info for garbage collection. Whenever a process dies, the Postmaster - * cleans up after it. Currently, NO information is required for cleanup, + * cleans up after it. Currently, NO information is required for cleanup, * but I left this structure around in case that changed. */ -typedef struct bkend { - int pid; /* process id of backend */ -} Backend; +typedef struct bkend +{ + int pid; /* process id of backend */ +} Backend; /* list of active backends. For garbage collection only now. */ -static Dllist* BackendList; +static Dllist *BackendList; /* list of ports associated with still open, but incomplete connections */ -static Dllist* PortList; +static Dllist *PortList; -static short PostPortName = -1; -static short ActiveBackends = FALSE; -static int NextBackendId = MAXINT; /* XXX why? */ -static char *progname = (char *) NULL; +static short PostPortName = -1; +static short ActiveBackends = FALSE; +static int NextBackendId = MAXINT; /* XXX why? */ +static char *progname = (char *) NULL; /* * Default Values */ -static char Execfile[MAXPATHLEN] = ""; +static char Execfile[MAXPATHLEN] = ""; -static int ServerSock = INVALID_SOCK; /* stream socket server */ +static int ServerSock = INVALID_SOCK; /* stream socket server */ /* * Set by the -o option */ -static char ExtraOptions[ARGV_SIZE] = ""; +static char ExtraOptions[ARGV_SIZE] = ""; /* * These globals control the behavior of the postmaster in case some - * backend dumps core. Normally, it kills all peers of the dead backend + * backend dumps core. Normally, it kills all peers of the dead backend * and reinitializes shared memory. By specifying -s or -n, we can have * the postmaster stop (rather than kill) peers and not reinitialize * shared data structures. */ -static int Reinit = 1; -static int SendStop = 0; +static int Reinit = 1; +static int SendStop = 0; -static int MultiplexedBackends = 0; -static int MultiplexedBackendPort; +static int MultiplexedBackends = 0; +static int MultiplexedBackendPort; -/* +/* * postmaster.c - function prototypes */ -static void pmdaemonize(void); -static void ConnStartup(Port *port, int *status, - char *errormsg, const int errormsg_len); -static int ConnCreate(int serverFd, int *newFdP); -static void reset_shared(short port); -static void pmdie(SIGNAL_ARGS); -static void reaper(SIGNAL_ARGS); -static void dumpstatus(SIGNAL_ARGS); -static void CleanupProc(int pid, int exitstatus); -static int DoExec(StartupInfo *packet, int portFd); -static void ExitPostmaster(int status); -static void usage(const char *); -static int ServerLoop(void); -static int BackendStartup(StartupInfo *packet, Port *port, int *pidPtr); -static void send_error_reply(Port *port, const char *errormsg); - -extern char *optarg; -extern int optind, opterr; +static void pmdaemonize(void); +static void +ConnStartup(Port * port, int *status, + char *errormsg, const int errormsg_len); +static int ConnCreate(int serverFd, int *newFdP); +static void reset_shared(short port); +static void pmdie(SIGNAL_ARGS); +static void reaper(SIGNAL_ARGS); +static void dumpstatus(SIGNAL_ARGS); +static void CleanupProc(int pid, int exitstatus); +static int DoExec(StartupInfo * packet, int portFd); +static void ExitPostmaster(int status); +static void usage(const char *); +static int ServerLoop(void); +static int BackendStartup(StartupInfo * packet, Port * port, int *pidPtr); +static void send_error_reply(Port * port, const char *errormsg); + +extern char *optarg; +extern int optind, + opterr; static void -checkDataDir(const char *DataDir, bool *DataDirOK) +checkDataDir(const char *DataDir, bool * DataDirOK) { - if (DataDir == NULL) { - fprintf(stderr, "%s does not know where to find the database system " - "data. You must specify the directory that contains the " - "database system either by specifying the -D invocation " - "option or by setting the PGDATA environment variable.\n\n", - progname); - *DataDirOK = false; - } else { - char path[MAXPATHLEN]; - FILE *fp; - - sprintf(path, "%s%cbase%ctemplate1%cpg_class", - DataDir, SEP_CHAR, SEP_CHAR, SEP_CHAR); - fp = fopen(path, "r"); - if (fp == NULL) { - fprintf(stderr, "%s does not find the database system. " - "Expected to find it " - "in the PGDATA directory \"%s\", but unable to open file " - "with pathname \"%s\".\n\n", - progname, DataDir, path); - *DataDirOK = false; - } else { - char *reason; - /* reason ValidatePgVersion failed. NULL if didn't */ - - fclose(fp); - - ValidatePgVersion(DataDir, &reason); - if (reason) { - fprintf(stderr, - "Database system in directory %s " - "is not compatible with this version of " - "Postgres, or we are unable to read the " - "PG_VERSION file. " - "Explanation from ValidatePgVersion: %s\n\n", - DataDir, reason); - free(reason); - *DataDirOK = false; - } else *DataDirOK = true; - } - } + if (DataDir == NULL) + { + fprintf(stderr, "%s does not know where to find the database system " + "data. You must specify the directory that contains the " + "database system either by specifying the -D invocation " + "option or by setting the PGDATA environment variable.\n\n", + progname); + *DataDirOK = false; + } + else + { + char path[MAXPATHLEN]; + FILE *fp; + + sprintf(path, "%s%cbase%ctemplate1%cpg_class", + DataDir, SEP_CHAR, SEP_CHAR, SEP_CHAR); + fp = fopen(path, "r"); + if (fp == NULL) + { + fprintf(stderr, "%s does not find the database system. " + "Expected to find it " + "in the PGDATA directory \"%s\", but unable to open file " + "with pathname \"%s\".\n\n", + progname, DataDir, path); + *DataDirOK = false; + } + else + { + char *reason; + + /* reason ValidatePgVersion failed. NULL if didn't */ + + fclose(fp); + + ValidatePgVersion(DataDir, &reason); + if (reason) + { + fprintf(stderr, + "Database system in directory %s " + "is not compatible with this version of " + "Postgres, or we are unable to read the " + "PG_VERSION file. " + "Explanation from ValidatePgVersion: %s\n\n", + DataDir, reason); + free(reason); + *DataDirOK = false; + } + else + *DataDirOK = true; + } + } } @@ -223,533 +238,596 @@ checkDataDir(const char *DataDir, bool *DataDirOK) int PostmasterMain(int argc, char *argv[]) { - extern int NBuffers; /* from buffer/bufmgr.c */ - extern bool IsPostmaster; /* from smgr/mm.c */ - int opt; - char *hostName; - int status; - int silentflag = 0; - char hostbuf[MAXHOSTNAMELEN]; - bool DataDirOK; /* We have a usable PGDATA value */ - - progname = argv[0]; - - IsPostmaster = true; - - /* for security, no dir or file created can be group or other accessible */ - umask((mode_t) 0077); - - if (!(hostName = getenv("PGHOST"))) { - if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0) - strcpy(hostbuf, "localhost"); - hostName = hostbuf; - } - - DataDir = getenv("PGDATA"); /* default value */ - - opterr = 0; - while ((opt = getopt(argc, argv, "a:B:b:D:dm:Mno:p:Ss")) != EOF) { - switch (opt) { - case 'a': - /* Set the authentication system. */ - be_setauthsvc(optarg); - break; - case 'B': - /* - * The number of buffers to create. Setting this - * option means we have to start each backend with - * a -B # to make sure they know how many buffers - * were allocated. - */ - NBuffers = atol(optarg); - strcat(ExtraOptions, " -B "); - strcat(ExtraOptions, optarg); - break; - case 'b': - /* Set the backend executable file to use. */ - if (!ValidateBackend(optarg)) - strcpy(Execfile, optarg); - else { - fprintf(stderr, "%s: invalid backend \"%s\"\n", - progname, optarg); - exit(2); - } - break; - case 'D': - /* Set PGDATA from the command line. */ - DataDir = optarg; - break; - case 'd': - /* - * Turn on debugging for the postmaster and the backend - * servers descended from it. - */ - if ((optind < argc) && *argv[optind] != '-') { - DebugLvl = atoi(argv[optind]); - optind++; - } - else - DebugLvl = 1; - break; - case 'm': - MultiplexedBackends = 1; - MultiplexedBackendPort = atoi(optarg); - break; - case 'M': - /* ignore this flag. This may be passed in because the - program was run as 'postgres -M' instead of 'postmaster' */ - break; - case 'n': - /* Don't reinit shared mem after abnormal exit */ - Reinit = 0; - break; - case 'o': - /* - * Other options to pass to the backend on the - * command line -- useful only for debugging. - */ - strcat(ExtraOptions, " "); - strcat(ExtraOptions, optarg); - break; - case 'p': - /* Set PGPORT by hand. */ - PostPortName = (short) atoi(optarg); - break; - case 'S': - /* - * Start in 'S'ilent mode (disassociate from controlling tty). - * You may also think of this as 'S'ysV mode since it's most - * badly needed on SysV-derived systems like SVR4 and HP-UX. - */ - silentflag = 1; - break; - case 's': - /* - * In the event that some backend dumps core, - * send SIGSTOP, rather than SIGUSR1, to all - * its peers. This lets the wily post_hacker - * collect core dumps from everyone. - */ - SendStop = 1; - break; - default: - /* usage() never returns */ - usage(progname); - break; - } - } - if (PostPortName == -1) - PostPortName = pq_getport(); - - checkDataDir(DataDir, &DataDirOK); /* issues error messages */ - if (!DataDirOK) { - fprintf(stderr, "No data directory -- can't proceed.\n"); - exit(2); - } - - if (!Execfile[0] && FindBackend(Execfile, argv[0]) < 0) { - fprintf(stderr, "%s: could not find backend to execute...\n", - argv[0]); - exit(1); - } - - - status = StreamServerPort(hostName, PostPortName, &ServerSock); - if (status != STATUS_OK) { - fprintf(stderr, "%s: cannot create stream port\n", - progname); - exit(1); - } - - /* set up shared memory and semaphores */ - EnableMemoryContext(TRUE); - reset_shared(PostPortName); - - /* - * Initialize the list of active backends. This list is only - * used for garbage collecting the backend processes. - */ - BackendList = DLNewList(); - PortList = DLNewList(); - - if (silentflag) - pmdaemonize(); - - pqsignal(SIGINT, pmdie); - pqsignal(SIGCHLD, reaper); - pqsignal(SIGTTIN, SIG_IGN); - pqsignal(SIGTTOU, SIG_IGN); - pqsignal(SIGHUP, pmdie); - pqsignal(SIGTERM, pmdie); - pqsignal(SIGCONT, dumpstatus); - pqsignal(SIGPIPE, SIG_IGN); - - status = ServerLoop(); - - ExitPostmaster(status != STATUS_OK); - return 0; /* not reached */ + extern int NBuffers; /* from buffer/bufmgr.c */ + extern bool IsPostmaster; /* from smgr/mm.c */ + int opt; + char *hostName; + int status; + int silentflag = 0; + char hostbuf[MAXHOSTNAMELEN]; + bool DataDirOK; /* We have a usable PGDATA value */ + + progname = argv[0]; + + IsPostmaster = true; + + /* + * for security, no dir or file created can be group or other + * accessible + */ + umask((mode_t) 0077); + + if (!(hostName = getenv("PGHOST"))) + { + if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0) + strcpy(hostbuf, "localhost"); + hostName = hostbuf; + } + + DataDir = getenv("PGDATA"); /* default value */ + + opterr = 0; + while ((opt = getopt(argc, argv, "a:B:b:D:dm:Mno:p:Ss")) != EOF) + { + switch (opt) + { + case 'a': + /* Set the authentication system. */ + be_setauthsvc(optarg); + break; + case 'B': + + /* + * The number of buffers to create. Setting this option means + * we have to start each backend with a -B # to make sure they + * know how many buffers were allocated. + */ + NBuffers = atol(optarg); + strcat(ExtraOptions, " -B "); + strcat(ExtraOptions, optarg); + break; + case 'b': + /* Set the backend executable file to use. */ + if (!ValidateBackend(optarg)) + strcpy(Execfile, optarg); + else + { + fprintf(stderr, "%s: invalid backend \"%s\"\n", + progname, optarg); + exit(2); + } + break; + case 'D': + /* Set PGDATA from the command line. */ + DataDir = optarg; + break; + case 'd': + + /* + * Turn on debugging for the postmaster and the backend + * servers descended from it. + */ + if ((optind < argc) && *argv[optind] != '-') + { + DebugLvl = atoi(argv[optind]); + optind++; + } + else + DebugLvl = 1; + break; + case 'm': + MultiplexedBackends = 1; + MultiplexedBackendPort = atoi(optarg); + break; + case 'M': + + /* + * ignore this flag. This may be passed in because the + * program was run as 'postgres -M' instead of 'postmaster' + */ + break; + case 'n': + /* Don't reinit shared mem after abnormal exit */ + Reinit = 0; + break; + case 'o': + + /* + * Other options to pass to the backend on the command line -- + * useful only for debugging. + */ + strcat(ExtraOptions, " "); + strcat(ExtraOptions, optarg); + break; + case 'p': + /* Set PGPORT by hand. */ + PostPortName = (short) atoi(optarg); + break; + case 'S': + + /* + * Start in 'S'ilent mode (disassociate from controlling tty). + * You may also think of this as 'S'ysV mode since it's most + * badly needed on SysV-derived systems like SVR4 and HP-UX. + */ + silentflag = 1; + break; + case 's': + + /* + * In the event that some backend dumps core, send SIGSTOP, + * rather than SIGUSR1, to all its peers. This lets the wily + * post_hacker collect core dumps from everyone. + */ + SendStop = 1; + break; + default: + /* usage() never returns */ + usage(progname); + break; + } + } + if (PostPortName == -1) + PostPortName = pq_getport(); + + checkDataDir(DataDir, &DataDirOK); /* issues error messages */ + if (!DataDirOK) + { + fprintf(stderr, "No data directory -- can't proceed.\n"); + exit(2); + } + + if (!Execfile[0] && FindBackend(Execfile, argv[0]) < 0) + { + fprintf(stderr, "%s: could not find backend to execute...\n", + argv[0]); + exit(1); + } + + + status = StreamServerPort(hostName, PostPortName, &ServerSock); + if (status != STATUS_OK) + { + fprintf(stderr, "%s: cannot create stream port\n", + progname); + exit(1); + } + + /* set up shared memory and semaphores */ + EnableMemoryContext(TRUE); + reset_shared(PostPortName); + + /* + * Initialize the list of active backends. This list is only used for + * garbage collecting the backend processes. + */ + BackendList = DLNewList(); + PortList = DLNewList(); + + if (silentflag) + pmdaemonize(); + + pqsignal(SIGINT, pmdie); + pqsignal(SIGCHLD, reaper); + pqsignal(SIGTTIN, SIG_IGN); + pqsignal(SIGTTOU, SIG_IGN); + pqsignal(SIGHUP, pmdie); + pqsignal(SIGTERM, pmdie); + pqsignal(SIGCONT, dumpstatus); + pqsignal(SIGPIPE, SIG_IGN); + + status = ServerLoop(); + + ExitPostmaster(status != STATUS_OK); + return 0; /* not reached */ } static void pmdaemonize(void) { - int i; - - if (fork()) - exit(0); + int i; + + if (fork()) + exit(0); /* GH: If there's no setsid(), we hopefully don't need silent mode. * Until there's a better solution. */ #ifdef HAVE_SETSID - if (setsid() < 0) { - fprintf(stderr, "%s: ", progname); - perror("cannot disassociate from controlling TTY"); - exit(1); - } + if (setsid() < 0) + { + fprintf(stderr, "%s: ", progname); + perror("cannot disassociate from controlling TTY"); + exit(1); + } #endif - i = open(NULL_DEV, O_RDWR); - dup2(i, 0); - dup2(i, 1); - dup2(i, 2); - close(i); + i = open(NULL_DEV, O_RDWR); + dup2(i, 0); + dup2(i, 1); + dup2(i, 2); + close(i); } static void usage(const char *progname) { - fprintf(stderr, "usage: %s [options..]\n", progname); - fprintf(stderr, "\t-a authsys\tdo/do not permit use of an authentication system\n"); - fprintf(stderr, "\t-B nbufs\tset number of shared buffers\n"); - fprintf(stderr, "\t-b backend\tuse a specific backend server executable\n"); - fprintf(stderr, "\t-d [1|2|3]\tset debugging level\n"); - fprintf(stderr, "\t-D datadir\tset data directory\n"); - fprintf(stderr, "\t-m \tstart up multiplexing backends\n"); - fprintf(stderr, "\t-n\t\tdon't reinitialize shared memory after abnormal exit\n"); - fprintf(stderr, "\t-o option\tpass 'option' to each backend servers\n"); - fprintf(stderr, "\t-p port\t\tspecify port for postmaster to listen on\n"); - fprintf(stderr, "\t-S\t\tsilent mode (disassociate from tty)\n"); - fprintf(stderr, "\t-s\t\tsend SIGSTOP to all backend servers if one dies\n"); - exit(1); + fprintf(stderr, "usage: %s [options..]\n", progname); + fprintf(stderr, "\t-a authsys\tdo/do not permit use of an authentication system\n"); + fprintf(stderr, "\t-B nbufs\tset number of shared buffers\n"); + fprintf(stderr, "\t-b backend\tuse a specific backend server executable\n"); + fprintf(stderr, "\t-d [1|2|3]\tset debugging level\n"); + fprintf(stderr, "\t-D datadir\tset data directory\n"); + fprintf(stderr, "\t-m \tstart up multiplexing backends\n"); + fprintf(stderr, "\t-n\t\tdon't reinitialize shared memory after abnormal exit\n"); + fprintf(stderr, "\t-o option\tpass 'option' to each backend servers\n"); + fprintf(stderr, "\t-p port\t\tspecify port for postmaster to listen on\n"); + fprintf(stderr, "\t-S\t\tsilent mode (disassociate from tty)\n"); + fprintf(stderr, "\t-s\t\tsend SIGSTOP to all backend servers if one dies\n"); + exit(1); } static int ServerLoop(void) { - int serverFd = ServerSock; - fd_set rmask, basemask; - int nSockets, nSelected, status, newFd; - Dlelem *next, *curr; - /* GH: For !HAVE_SIGPROCMASK (NEXTSTEP), TRH implemented - * an alternative interface. - */ + int serverFd = ServerSock; + fd_set rmask, + basemask; + int nSockets, + nSelected, + status, + newFd; + Dlelem *next, + *curr; + + /* + * GH: For !HAVE_SIGPROCMASK (NEXTSTEP), TRH implemented an + * alternative interface. + */ #ifdef HAVE_SIGPROCMASK - sigset_t oldsigmask, newsigmask; + sigset_t oldsigmask, + newsigmask; + #else - int orgsigmask = sigblock(0); + int orgsigmask = sigblock(0); + #endif - - nSockets = ServerSock + 1; - FD_ZERO(&basemask); - FD_SET(ServerSock, &basemask); - + + nSockets = ServerSock + 1; + FD_ZERO(&basemask); + FD_SET(ServerSock, &basemask); + #ifdef HAVE_SIGPROCMASK - sigprocmask(0,0,&oldsigmask); - sigemptyset(&newsigmask); - sigaddset(&newsigmask,SIGCHLD); + sigprocmask(0, 0, &oldsigmask); + sigemptyset(&newsigmask); + sigaddset(&newsigmask, SIGCHLD); #endif - for (;;) { + for (;;) + { #ifdef HAVE_SIGPROCMASK - sigprocmask(SIG_SETMASK,&oldsigmask,0); + sigprocmask(SIG_SETMASK, &oldsigmask, 0); #else - sigsetmask(orgsigmask); + sigsetmask(orgsigmask); #endif - newFd = -1; - memmove((char *) &rmask, (char *) &basemask, sizeof(fd_set)); - if ((nSelected = select(nSockets, &rmask, - (fd_set *) NULL, - (fd_set *) NULL, - (struct timeval *) NULL)) < 0) { - if (errno == EINTR) - continue; - fprintf(stderr, "%s: ServerLoop: select failed\n", - progname); - return(STATUS_ERROR); - } - /* [TRH] - * To avoid race conditions, block SIGCHLD signals while we are - * handling the request. (both reaper() and ConnCreate() - * manipulate the BackEnd list, and reaper() calls free() which is - * usually non-reentrant.) - */ + newFd = -1; + memmove((char *) &rmask, (char *) &basemask, sizeof(fd_set)); + if ((nSelected = select(nSockets, &rmask, + (fd_set *) NULL, + (fd_set *) NULL, + (struct timeval *) NULL)) < 0) + { + if (errno == EINTR) + continue; + fprintf(stderr, "%s: ServerLoop: select failed\n", + progname); + return (STATUS_ERROR); + } + + /* + * [TRH] To avoid race conditions, block SIGCHLD signals while we + * are handling the request. (both reaper() and ConnCreate() + * manipulate the BackEnd list, and reaper() calls free() which is + * usually non-reentrant.) + */ #ifdef HAVE_SIGPROCMASK - sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask); + sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask); #else - sigblock(sigmask(SIGCHLD)); /* XXX[TRH] portability */ + sigblock(sigmask(SIGCHLD)); /* XXX[TRH] portability */ #endif - if (DebugLvl > 1) { - fprintf(stderr, "%s: ServerLoop: %d sockets pending\n", - progname, nSelected); - } - - /* new connection pending on our well-known port's socket */ - if (FD_ISSET(ServerSock, &rmask)) { - /* - * connect and make an addition to PortList. If - * the connection dies and we notice it, just forget - * about the whole thing. - */ - if (ConnCreate(serverFd, &newFd) == STATUS_OK) { - if (newFd >= nSockets) - nSockets = newFd + 1; - FD_SET(newFd, &rmask); - FD_SET(newFd, &basemask); - if (DebugLvl) - fprintf(stderr, "%s: ServerLoop: connect on %d\n", - progname, newFd); - } - --nSelected; - FD_CLR(ServerSock, &rmask); - } - - if (DebugLvl > 1) { - fprintf(stderr, "%s: ServerLoop:\tnSelected=%d\n", - progname, nSelected); - curr = DLGetHead(PortList); - while (curr) { - Port *port = DLE_VAL(curr); - - fprintf(stderr, "%s: ServerLoop:\t\tport %d%s pending\n", - progname, port->sock, - FD_ISSET(port->sock, &rmask) - ? "" : - " not"); - curr = DLGetSucc(curr); - } - } - - curr = DLGetHead(PortList); - - while (curr) { - Port *port = (Port*)DLE_VAL(curr); - int lastbytes = port->nBytes; - - if (FD_ISSET(port->sock, &rmask) && port->sock != newFd) { - if (DebugLvl > 1) - fprintf(stderr, "%s: ServerLoop:\t\thandling %d\n", - progname, port->sock); - --nSelected; - - /* - * Read the incoming packet into its packet buffer. - * Read the connection id out of the packet so we - * know who the packet is from. - */ - status = PacketReceive(port, &port->buf, NON_BLOCKING); - switch (status) { - case STATUS_OK: { - int CSstatus; /* Completion status of ConnStartup */ - char errormsg[200]; /* error msg from ConnStartup */ - - ConnStartup(port, &CSstatus, errormsg, sizeof(errormsg)); - - if (CSstatus == STATUS_ERROR) - send_error_reply(port, errormsg); - ActiveBackends = TRUE; - } - /*FALLTHROUGH*/ - case STATUS_INVALID: - if (DebugLvl) - fprintf(stderr, "%s: ServerLoop:\t\tdone with %d\n", - progname, port->sock); - break; - case STATUS_BAD_PACKET: - /* - * This is a bogus client, kill the connection - * and forget the whole thing. - */ - if (DebugLvl) - fprintf(stderr, "%s: ServerLoop:\t\tbad packet format (reported packet size of %d read on port %d\n", progname, port->nBytes, port->sock); - break; - case STATUS_NOT_DONE: - if (DebugLvl) - fprintf(stderr, "%s: ServerLoop:\t\tpartial packet (%d bytes actually read) on %d\n", - progname, port->nBytes, port->sock); - /* - * If we've received at least a PacketHdr's worth of data - * and we're still receiving data each time we read, we're - * ok. If the client gives us less than a PacketHdr at - * the beginning, just kill the connection and forget - * about the whole thing. - */ - if (lastbytes < port->nBytes) { - if (DebugLvl) - fprintf(stderr, "%s: ServerLoop:\t\tpartial packet on %d ok\n", - progname, port->sock); - curr = DLGetSucc(curr); - continue; - } - break; - case STATUS_ERROR: /* system call error - die */ - fprintf(stderr, "%s: ServerLoop:\t\terror receiving packet\n", - progname); - return(STATUS_ERROR); - } - FD_CLR(port->sock, &basemask); - StreamClose(port->sock); - next = DLGetSucc(curr); - DLRemove(curr); - free(port); - DLFreeElem(curr); - curr = next; - continue; - } - curr = DLGetSucc(curr); - } - Assert(nSelected == 0); - } + if (DebugLvl > 1) + { + fprintf(stderr, "%s: ServerLoop: %d sockets pending\n", + progname, nSelected); + } + + /* new connection pending on our well-known port's socket */ + if (FD_ISSET(ServerSock, &rmask)) + { + + /* + * connect and make an addition to PortList. If the + * connection dies and we notice it, just forget about the + * whole thing. + */ + if (ConnCreate(serverFd, &newFd) == STATUS_OK) + { + if (newFd >= nSockets) + nSockets = newFd + 1; + FD_SET(newFd, &rmask); + FD_SET(newFd, &basemask); + if (DebugLvl) + fprintf(stderr, "%s: ServerLoop: connect on %d\n", + progname, newFd); + } + --nSelected; + FD_CLR(ServerSock, &rmask); + } + + if (DebugLvl > 1) + { + fprintf(stderr, "%s: ServerLoop:\tnSelected=%d\n", + progname, nSelected); + curr = DLGetHead(PortList); + while (curr) + { + Port *port = DLE_VAL(curr); + + fprintf(stderr, "%s: ServerLoop:\t\tport %d%s pending\n", + progname, port->sock, + FD_ISSET(port->sock, &rmask) + ? "" : + " not"); + curr = DLGetSucc(curr); + } + } + + curr = DLGetHead(PortList); + + while (curr) + { + Port *port = (Port *) DLE_VAL(curr); + int lastbytes = port->nBytes; + + if (FD_ISSET(port->sock, &rmask) && port->sock != newFd) + { + if (DebugLvl > 1) + fprintf(stderr, "%s: ServerLoop:\t\thandling %d\n", + progname, port->sock); + --nSelected; + + /* + * Read the incoming packet into its packet buffer. Read + * the connection id out of the packet so we know who the + * packet is from. + */ + status = PacketReceive(port, &port->buf, NON_BLOCKING); + switch (status) + { + case STATUS_OK: + { + int CSstatus; /* Completion status of + * ConnStartup */ + char errormsg[200]; /* error msg from + * ConnStartup */ + + ConnStartup(port, &CSstatus, errormsg, sizeof(errormsg)); + + if (CSstatus == STATUS_ERROR) + send_error_reply(port, errormsg); + ActiveBackends = TRUE; + } + /* FALLTHROUGH */ + case STATUS_INVALID: + if (DebugLvl) + fprintf(stderr, "%s: ServerLoop:\t\tdone with %d\n", + progname, port->sock); + break; + case STATUS_BAD_PACKET: + + /* + * This is a bogus client, kill the connection and + * forget the whole thing. + */ + if (DebugLvl) + fprintf(stderr, "%s: ServerLoop:\t\tbad packet format (reported packet size of %d read on port %d\n", progname, port->nBytes, port->sock); + break; + case STATUS_NOT_DONE: + if (DebugLvl) + fprintf(stderr, "%s: ServerLoop:\t\tpartial packet (%d bytes actually read) on %d\n", + progname, port->nBytes, port->sock); + + /* + * If we've received at least a PacketHdr's worth of + * data and we're still receiving data each time we + * read, we're ok. If the client gives us less than a + * PacketHdr at the beginning, just kill the + * connection and forget about the whole thing. + */ + if (lastbytes < port->nBytes) + { + if (DebugLvl) + fprintf(stderr, "%s: ServerLoop:\t\tpartial packet on %d ok\n", + progname, port->sock); + curr = DLGetSucc(curr); + continue; + } + break; + case STATUS_ERROR: /* system call error - die */ + fprintf(stderr, "%s: ServerLoop:\t\terror receiving packet\n", + progname); + return (STATUS_ERROR); + } + FD_CLR(port->sock, &basemask); + StreamClose(port->sock); + next = DLGetSucc(curr); + DLRemove(curr); + free(port); + DLFreeElem(curr); + curr = next; + continue; + } + curr = DLGetSucc(curr); + } + Assert(nSelected == 0); + } } /* - ConnStartup: get the startup packet from the front end (client), - authenticate the user, and start up a backend. + ConnStartup: get the startup packet from the front end (client), + authenticate the user, and start up a backend. - If all goes well, return *status == STATUS_OK. - Otherwise, return *status == STATUS_ERROR and return a text string - explaining why in the "errormsg_len" bytes at "errormsg", + If all goes well, return *status == STATUS_OK. + Otherwise, return *status == STATUS_ERROR and return a text string + explaining why in the "errormsg_len" bytes at "errormsg", */ static void -ConnStartup(Port *port, int *status, - char *errormsg, const int errormsg_len) +ConnStartup(Port * port, int *status, + char *errormsg, const int errormsg_len) { - MsgType msgType; - char namebuf[NAMEDATALEN]; - int pid; - PacketBuf *p; - StartupInfo sp; - char *tmp; - - p = &port->buf; - - sp.database[0]='\0'; - sp.user[0]='\0'; - sp.options[0]='\0'; - sp.execFile[0]='\0'; - sp.tty[0]='\0'; - - tmp= p->data; - strncpy(sp.database,tmp,sizeof(sp.database)); - tmp += sizeof(sp.database); - strncpy(sp.user,tmp, sizeof(sp.user)); - tmp += sizeof(sp.user); - strncpy(sp.options,tmp, sizeof(sp.options)); - tmp += sizeof(sp.options); - strncpy(sp.execFile,tmp, sizeof(sp.execFile)); - tmp += sizeof(sp.execFile); - strncpy(sp.tty,tmp, sizeof(sp.tty)); - - msgType = (MsgType) ntohl(port->buf.msgtype); - - strNcpy(namebuf, sp.user, NAMEDATALEN-1); - if (!namebuf[0]) { - strncpy(errormsg, - "No Postgres username specified in startup packet.", - errormsg_len); - *status = STATUS_ERROR; - } else { - if (be_recvauth(msgType, port, namebuf, &sp) != STATUS_OK) { - char buffer[200 + sizeof(namebuf)]; - sprintf(buffer, - "Failed to authenticate client as Postgres user '%s' " - "using %s: %s", - namebuf, name_of_authentication_type(msgType), PQerrormsg); - strncpy(errormsg, buffer, errormsg_len); - *status = STATUS_ERROR; - } else { - if (BackendStartup(&sp, port, &pid) != STATUS_OK) { - strncpy(errormsg, "Startup (fork) of backend failed.", - errormsg_len); - *status = STATUS_ERROR; - } else { - errormsg[0] = '\0'; /* just for robustness */ - *status = STATUS_OK; - } - } - } - if (*status == STATUS_ERROR) - fprintf(stderr, "%s: ConnStartup: %s\n", progname, errormsg); + MsgType msgType; + char namebuf[NAMEDATALEN]; + int pid; + PacketBuf *p; + StartupInfo sp; + char *tmp; + + p = &port->buf; + + sp.database[0] = '\0'; + sp.user[0] = '\0'; + sp.options[0] = '\0'; + sp.execFile[0] = '\0'; + sp.tty[0] = '\0'; + + tmp = p->data; + strncpy(sp.database, tmp, sizeof(sp.database)); + tmp += sizeof(sp.database); + strncpy(sp.user, tmp, sizeof(sp.user)); + tmp += sizeof(sp.user); + strncpy(sp.options, tmp, sizeof(sp.options)); + tmp += sizeof(sp.options); + strncpy(sp.execFile, tmp, sizeof(sp.execFile)); + tmp += sizeof(sp.execFile); + strncpy(sp.tty, tmp, sizeof(sp.tty)); + + msgType = (MsgType) ntohl(port->buf.msgtype); + + strNcpy(namebuf, sp.user, NAMEDATALEN - 1); + if (!namebuf[0]) + { + strncpy(errormsg, + "No Postgres username specified in startup packet.", + errormsg_len); + *status = STATUS_ERROR; + } + else + { + if (be_recvauth(msgType, port, namebuf, &sp) != STATUS_OK) + { + char buffer[200 + sizeof(namebuf)]; + + sprintf(buffer, + "Failed to authenticate client as Postgres user '%s' " + "using %s: %s", + namebuf, name_of_authentication_type(msgType), PQerrormsg); + strncpy(errormsg, buffer, errormsg_len); + *status = STATUS_ERROR; + } + else + { + if (BackendStartup(&sp, port, &pid) != STATUS_OK) + { + strncpy(errormsg, "Startup (fork) of backend failed.", + errormsg_len); + *status = STATUS_ERROR; + } + else + { + errormsg[0] = '\0'; /* just for robustness */ + *status = STATUS_OK; + } + } + } + if (*status == STATUS_ERROR) + fprintf(stderr, "%s: ConnStartup: %s\n", progname, errormsg); } /* - send_error_reply: send a reply to the front end telling it that - the connection was a bust, and why. - - "port" tells to whom and how to send the reply. "errormsg" is - the string of text telling what the problem was. - - It should be noted that we're executing a pretty messy protocol - here. The postmaster does not reply when the connection is - successful, but rather just hands the connection off to the - backend and the backend waits for a query from the frontend. - Thus, the frontend is not expecting any reply in regards to the - connect request. - - But when the connection fails, we send this reply that starts - with "E". The frontend only gets this reply when it sends its - first query and waits for the reply. Nobody receives that query, - but this reply is already in the pipe, so that's what the - frontend sees. - - Note that the backend closes the socket immediately after sending - the reply, so to give the frontend a fighting chance to see the - error info, we set the socket to linger up to 3 seconds waiting - for the frontend to retrieve the message. That's all the delay - we can afford, since we have other clients to serve and the - postmaster will be blocked the whole time. Also, if there is no - message space in the socket for the reply (shouldn't be a - problem) the postmaster will block until the frontend reads the - reply. + send_error_reply: send a reply to the front end telling it that + the connection was a bust, and why. + + "port" tells to whom and how to send the reply. "errormsg" is + the string of text telling what the problem was. + + It should be noted that we're executing a pretty messy protocol + here. The postmaster does not reply when the connection is + successful, but rather just hands the connection off to the + backend and the backend waits for a query from the frontend. + Thus, the frontend is not expecting any reply in regards to the + connect request. + + But when the connection fails, we send this reply that starts + with "E". The frontend only gets this reply when it sends its + first query and waits for the reply. Nobody receives that query, + but this reply is already in the pipe, so that's what the + frontend sees. + + Note that the backend closes the socket immediately after sending + the reply, so to give the frontend a fighting chance to see the + error info, we set the socket to linger up to 3 seconds waiting + for the frontend to retrieve the message. That's all the delay + we can afford, since we have other clients to serve and the + postmaster will be blocked the whole time. Also, if there is no + message space in the socket for the reply (shouldn't be a + problem) the postmaster will block until the frontend reads the + reply. */ static void -send_error_reply(Port *port, const char *errormsg) +send_error_reply(Port * port, const char *errormsg) { - int rc; /* return code from sendto */ - char *reply; - /* The literal reply string we put into the socket. This is a pointer - to storage we malloc. - */ - const struct linger linger_parm = {true, LINGER_TIME}; - /* A parameter for setsockopt() that tells it to have close() block for - a while waiting for the frontend to read its outstanding messages. - */ - - reply = malloc(strlen(errormsg)+10); - - sprintf(reply, "E%s", errormsg); - - rc = send(port->sock, (Addr) reply, strlen(reply)+1, /* flags */ 0); - if (rc < 0) - fprintf(stderr, - "%s: ServerLoop:\t\t" - "Failed to send error reply to front end\n", - progname); - else if (rc < strlen(reply)+1) - fprintf(stderr, - "%s: ServerLoop:\t\t" - "Only partial error reply sent to front end.\n", - progname); - - free(reply); - /* Now we have to make sure frontend has a chance to see what we - just wrote. - */ - rc = setsockopt(port->sock, SOL_SOCKET, SO_LINGER, - &linger_parm, sizeof(linger_parm)); + int rc; /* return code from sendto */ + char *reply; + + /* + * The literal reply string we put into the socket. This is a pointer + * to storage we malloc. + */ + const struct linger linger_parm = {true, LINGER_TIME}; + + /* + * A parameter for setsockopt() that tells it to have close() block + * for a while waiting for the frontend to read its outstanding + * messages. + */ + + reply = malloc(strlen(errormsg) + 10); + + sprintf(reply, "E%s", errormsg); + + rc = send(port->sock, (Addr) reply, strlen(reply) + 1, /* flags */ 0); + if (rc < 0) + fprintf(stderr, + "%s: ServerLoop:\t\t" + "Failed to send error reply to front end\n", + progname); + else if (rc < strlen(reply) + 1) + fprintf(stderr, + "%s: ServerLoop:\t\t" + "Only partial error reply sent to front end.\n", + progname); + + free(reply); + + /* + * Now we have to make sure frontend has a chance to see what we just + * wrote. + */ + rc = setsockopt(port->sock, SOL_SOCKET, SO_LINGER, + &linger_parm, sizeof(linger_parm)); } @@ -759,26 +837,29 @@ send_error_reply(Port *port, const char *errormsg) static int ConnCreate(int serverFd, int *newFdP) { - int status; - Port *port; - - - if (!(port = (Port *) calloc(1, sizeof(Port)))) { - fprintf(stderr, "%s: ConnCreate: malloc failed\n", - progname); - ExitPostmaster(1); - } - - if ((status = StreamConnection(serverFd, port)) != STATUS_OK) { - StreamClose(port->sock); - free(port); - } - else { - DLAddHead(PortList, DLNewElem(port)); - *newFdP = port->sock; - } - - return (status); + int status; + Port *port; + + + if (!(port = (Port *) calloc(1, sizeof(Port)))) + { + fprintf(stderr, "%s: ConnCreate: malloc failed\n", + progname); + ExitPostmaster(1); + } + + if ((status = StreamConnection(serverFd, port)) != STATUS_OK) + { + StreamClose(port->sock); + free(port); + } + else + { + DLAddHead(PortList, DLNewElem(port)); + *newFdP = port->sock; + } + + return (status); } /* @@ -787,11 +868,11 @@ ConnCreate(int serverFd, int *newFdP) static void reset_shared(short port) { - IPCKey key; - - key = SystemPortAddressCreateIPCKey((SystemPortAddress) port); - CreateSharedMemoryAndSemaphores(key); - ActiveBackends = FALSE; + IPCKey key; + + key = SystemPortAddressCreateIPCKey((SystemPortAddress) port); + CreateSharedMemoryAndSemaphores(key); + ActiveBackends = FALSE; } /* @@ -800,7 +881,7 @@ reset_shared(short port) static void pmdie(SIGNAL_ARGS) { - exitpg(0); + exitpg(0); } /* @@ -811,25 +892,29 @@ reaper(SIGNAL_ARGS) { /* GH: replace waitpid for !HAVE_WAITPID. Does this work ? */ #ifdef HAVE_WAITPID - int status; /* backend exit status */ + int status; /* backend exit status */ + #else - union wait statusp; /* backend exit status */ + union wait statusp; /* backend exit status */ + #endif - int pid; /* process id of dead backend */ - - if (DebugLvl) - fprintf(stderr, "%s: reaping dead processes...\n", - progname); + int pid; /* process id of dead backend */ + + if (DebugLvl) + fprintf(stderr, "%s: reaping dead processes...\n", + progname); #ifdef HAVE_WAITPID - while((pid = waitpid(-1, &status, WNOHANG)) > 0) { - CleanupProc(pid, status); - pqsignal(SIGCHLD, reaper); - } + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + { + CleanupProc(pid, status); + pqsignal(SIGCHLD, reaper); + } #else - while((pid = wait3(&statusp, WNOHANG, NULL)) > 0) { - CleanupProc(pid, statusp.w_status); - pqsignal(SIGCHLD, reaper); - } + while ((pid = wait3(&statusp, WNOHANG, NULL)) > 0) + { + CleanupProc(pid, statusp.w_status); + pqsignal(SIGCHLD, reaper); + } #endif } @@ -842,189 +927,204 @@ reaper(SIGNAL_ARGS) */ static void CleanupProc(int pid, - int exitstatus) /* child's exit status. */ + int exitstatus) /* child's exit status. */ { - Dlelem *prev, *curr; - Backend *bp; - int sig; - - if (DebugLvl) { - fprintf(stderr, "%s: CleanupProc: pid %d exited with status %d\n", - progname, pid, exitstatus); - } - /* - * ------------------------- - * If a backend dies in an ugly way (i.e. exit status not 0) then - * we must signal all other backends to quickdie. If exit status - * is zero we assume everything is hunky dory and simply remove the - * backend from the active backend list. - * ------------------------- - */ - if (!exitstatus) { - curr = DLGetHead(BackendList); - while (curr) { - bp = (Backend*)DLE_VAL(curr); - if (bp->pid == pid) { - DLRemove(curr); - free(bp); - DLFreeElem(curr); - break; - } - curr = DLGetSucc(curr); - } - - ProcRemove(pid); - - return; - } - - curr = DLGetHead(BackendList); - while (curr) { - bp = (Backend*)DLE_VAL(curr); - - /* - * ----------------- - * SIGUSR1 is the special signal that sez exit without exitpg - * and let the user know what's going on. ProcSemaphoreKill() - * cleans up the backends semaphore. If SendStop is set (-s on - * command line), then we send a SIGSTOP so that we can - * core dumps from all backends by hand. - * ----------------- - */ - sig = (SendStop) ? SIGSTOP : SIGUSR1; - if (bp->pid != pid) { - if (DebugLvl) - fprintf(stderr, "%s: CleanupProc: sending %s to process %d\n", - progname, - (sig == SIGUSR1) - ? "SIGUSR1" : "SIGSTOP", - bp->pid); - kill(bp->pid, sig); - } - ProcRemove(bp->pid); - - prev = DLGetPred(curr); - DLRemove(curr); - free(bp); - DLFreeElem(curr); - if (!prev) { /* removed head */ - curr = DLGetHead(BackendList); - continue; - } - curr = DLGetSucc(prev); - } - /* - * ------------- - * Quasi_exit means run all of the on_exitpg routines but don't - * acutally call exit(). The on_exit list of routines to do is - * also truncated. - * - * Nothing up my sleeve here, ActiveBackends means that since the - * last time we recreated shared memory and sems another frontend - * has requested and received a connection and I have forked off - * another backend. This prevents me from reinitializing shared - * stuff more than once for the set of backends that caused the - * failure and were killed off. - * ---------------- - */ - if (ActiveBackends == TRUE && Reinit) { - if (DebugLvl) - fprintf(stderr, "%s: CleanupProc: reinitializing shared memory and semaphores\n", - progname); - quasi_exitpg(); - reset_shared(PostPortName); - } + Dlelem *prev, + *curr; + Backend *bp; + int sig; + + if (DebugLvl) + { + fprintf(stderr, "%s: CleanupProc: pid %d exited with status %d\n", + progname, pid, exitstatus); + } + + /* + * ------------------------- If a backend dies in an ugly way (i.e. + * exit status not 0) then we must signal all other backends to + * quickdie. If exit status is zero we assume everything is hunky + * dory and simply remove the backend from the active backend list. + * ------------------------- + */ + if (!exitstatus) + { + curr = DLGetHead(BackendList); + while (curr) + { + bp = (Backend *) DLE_VAL(curr); + if (bp->pid == pid) + { + DLRemove(curr); + free(bp); + DLFreeElem(curr); + break; + } + curr = DLGetSucc(curr); + } + + ProcRemove(pid); + + return; + } + + curr = DLGetHead(BackendList); + while (curr) + { + bp = (Backend *) DLE_VAL(curr); + + /* + * ----------------- SIGUSR1 is the special signal that sez exit + * without exitpg and let the user know what's going on. + * ProcSemaphoreKill() cleans up the backends semaphore. If + * SendStop is set (-s on command line), then we send a SIGSTOP so + * that we can core dumps from all backends by hand. + * ----------------- + */ + sig = (SendStop) ? SIGSTOP : SIGUSR1; + if (bp->pid != pid) + { + if (DebugLvl) + fprintf(stderr, "%s: CleanupProc: sending %s to process %d\n", + progname, + (sig == SIGUSR1) + ? "SIGUSR1" : "SIGSTOP", + bp->pid); + kill(bp->pid, sig); + } + ProcRemove(bp->pid); + + prev = DLGetPred(curr); + DLRemove(curr); + free(bp); + DLFreeElem(curr); + if (!prev) + { /* removed head */ + curr = DLGetHead(BackendList); + continue; + } + curr = DLGetSucc(prev); + } + + /* + * ------------- Quasi_exit means run all of the on_exitpg routines + * but don't acutally call exit(). The on_exit list of routines to do + * is also truncated. + * + * Nothing up my sleeve here, ActiveBackends means that since the last + * time we recreated shared memory and sems another frontend has + * requested and received a connection and I have forked off another + * backend. This prevents me from reinitializing shared stuff more + * than once for the set of backends that caused the failure and were + * killed off. ---------------- + */ + if (ActiveBackends == TRUE && Reinit) + { + if (DebugLvl) + fprintf(stderr, "%s: CleanupProc: reinitializing shared memory and semaphores\n", + progname); + quasi_exitpg(); + reset_shared(PostPortName); + } } /* * BackendStartup -- start backend process * * returns: STATUS_ERROR if the fork/exec failed, STATUS_OK - * otherwise. + * otherwise. * */ static int -BackendStartup(StartupInfo *packet, /* client's startup packet */ - Port *port, - int *pidPtr) +BackendStartup(StartupInfo * packet, /* client's startup packet */ + Port * port, + int *pidPtr) { - Backend* bn; /* for backend cleanup */ - int pid, i; - static char envEntry[4][2 * ARGV_SIZE]; - - for (i = 0; i < 4; ++i) { - memset(envEntry[i], 0, 2*ARGV_SIZE); - } - /* - * Set up the necessary environment variables for the backend - * This should really be some sort of message.... - */ - sprintf(envEntry[0], "POSTPORT=%d", PostPortName); - putenv(envEntry[0]); - sprintf(envEntry[1], "POSTID=%d", NextBackendId); - putenv(envEntry[1]); - sprintf(envEntry[2], "PG_USER=%s", packet->user); - putenv(envEntry[2]); - if (!getenv("PGDATA")) { - sprintf(envEntry[3], "PGDATA=%s", DataDir); - putenv(envEntry[3]); - } - if (DebugLvl > 2) { - char **p; - extern char **environ; - - fprintf(stderr, "%s: BackendStartup: environ dump:\n", - progname); - fprintf(stderr, "-----------------------------------------\n"); - for (p = environ; *p; ++p) - fprintf(stderr, "\t%s\n", *p); - fprintf(stderr, "-----------------------------------------\n"); - } - - if ((pid = FORK()) == 0) { /* child */ - if (DoExec(packet, port->sock)) - fprintf(stderr, "%s child[%d]: BackendStartup: execv failed\n", - progname, pid); - /* use _exit to keep from double-flushing stdio */ - _exit(1); - } - - /* in parent */ - if (pid < 0) { - fprintf(stderr, "%s: BackendStartup: fork failed\n", - progname); - return(STATUS_ERROR); - } - - if (DebugLvl) - fprintf(stderr, "%s: BackendStartup: pid %d user %s db %s socket %d\n", - progname, pid, packet->user, - (packet->database[0] == '\0' ? packet->user : packet->database), - port->sock); - - /* adjust backend counter */ - /* XXX Don't know why this is done, but for now backend needs it */ - NextBackendId -= 1; - - /* - * Everything's been successful, it's safe to add this backend to our - * list of backends. - */ - if (!(bn = (Backend *) calloc(1, sizeof (Backend)))) { - fprintf(stderr, "%s: BackendStartup: malloc failed\n", - progname); - ExitPostmaster(1); - } - - bn->pid = pid; - DLAddHead(BackendList,DLNewElem(bn)); - - if (MultiplexedBackends) - MultiplexedBackendPort++; - - *pidPtr = pid; - - return(STATUS_OK); + Backend *bn; /* for backend cleanup */ + int pid, + i; + static char envEntry[4][2 * ARGV_SIZE]; + + for (i = 0; i < 4; ++i) + { + memset(envEntry[i], 0, 2 * ARGV_SIZE); + } + + /* + * Set up the necessary environment variables for the backend This + * should really be some sort of message.... + */ + sprintf(envEntry[0], "POSTPORT=%d", PostPortName); + putenv(envEntry[0]); + sprintf(envEntry[1], "POSTID=%d", NextBackendId); + putenv(envEntry[1]); + sprintf(envEntry[2], "PG_USER=%s", packet->user); + putenv(envEntry[2]); + if (!getenv("PGDATA")) + { + sprintf(envEntry[3], "PGDATA=%s", DataDir); + putenv(envEntry[3]); + } + if (DebugLvl > 2) + { + char **p; + extern char **environ; + + fprintf(stderr, "%s: BackendStartup: environ dump:\n", + progname); + fprintf(stderr, "-----------------------------------------\n"); + for (p = environ; *p; ++p) + fprintf(stderr, "\t%s\n", *p); + fprintf(stderr, "-----------------------------------------\n"); + } + + if ((pid = FORK()) == 0) + { /* child */ + if (DoExec(packet, port->sock)) + fprintf(stderr, "%s child[%d]: BackendStartup: execv failed\n", + progname, pid); + /* use _exit to keep from double-flushing stdio */ + _exit(1); + } + + /* in parent */ + if (pid < 0) + { + fprintf(stderr, "%s: BackendStartup: fork failed\n", + progname); + return (STATUS_ERROR); + } + + if (DebugLvl) + fprintf(stderr, "%s: BackendStartup: pid %d user %s db %s socket %d\n", + progname, pid, packet->user, + (packet->database[0] == '\0' ? packet->user : packet->database), + port->sock); + + /* adjust backend counter */ + /* XXX Don't know why this is done, but for now backend needs it */ + NextBackendId -= 1; + + /* + * Everything's been successful, it's safe to add this backend to our + * list of backends. + */ + if (!(bn = (Backend *) calloc(1, sizeof(Backend)))) + { + fprintf(stderr, "%s: BackendStartup: malloc failed\n", + progname); + ExitPostmaster(1); + } + + bn->pid = pid; + DLAddHead(BackendList, DLNewElem(bn)); + + if (MultiplexedBackends) + MultiplexedBackendPort++; + + *pidPtr = pid; + + return (STATUS_OK); } /* @@ -1040,19 +1140,20 @@ BackendStartup(StartupInfo *packet, /* client's startup packet */ static void split_opts(char **argv, int *argcp, char *s) { - int i = *argcp; - - while (s && *s) { - while (isspace(*s)) - ++s; - if (*s) - argv[i++] = s; - while (*s && !isspace(*s)) - ++s; - if (isspace(*s)) - *s++ = '\0'; - } - *argcp = i; + int i = *argcp; + + while (s && *s) + { + while (isspace(*s)) + ++s; + if (*s) + argv[i++] = s; + while (*s && !isspace(*s)) + ++s; + if (isspace(*s)) + *s++ = '\0'; + } + *argcp = i; } /* @@ -1063,88 +1164,93 @@ split_opts(char **argv, int *argcp, char *s) * its thread back. (This is vfork() we're talking about. If we're using * fork() because we don't have vfork(), then we don't really care.) * - * returns: - * Shouldn't return at all. - * If execv() fails, return status. + * returns: + * Shouldn't return at all. + * If execv() fails, return status. */ static int -DoExec(StartupInfo *packet, int portFd) +DoExec(StartupInfo * packet, int portFd) { - char execbuf[MAXPATHLEN]; - char portbuf[ARGV_SIZE]; - char mbbuf[ARGV_SIZE]; - char debugbuf[ARGV_SIZE]; - char ttybuf[ARGV_SIZE + 1]; - char argbuf[(2 * ARGV_SIZE) + 1]; - /* - * each argument takes at least three chars, so we can't - * have more than ARGV_SIZE arguments in (2 * ARGV_SIZE) - * chars (i.e., packet->options plus ExtraOptions)... - */ - char *av[ARGV_SIZE]; - char dbbuf[ARGV_SIZE + 1]; - int ac = 0; - int i; - - strncpy(execbuf, Execfile, MAXPATHLEN-1); - av[ac++] = execbuf; - - /* Tell the backend it is being called from the postmaster */ - av[ac++] = "-p"; - - /* - * Pass the requested debugging level along to the backend. We - * decrement by one; level one debugging in the postmaster traces - * postmaster connection activity, and levels two and higher - * are passed along to the backend. This allows us to watch only - * the postmaster or the postmaster and the backend. - */ - - if (DebugLvl > 1) { - sprintf(debugbuf, "-d%d", DebugLvl); - av[ac++] = debugbuf; - } - else - av[ac++] = "-Q"; - - /* Pass the requested debugging output file */ - if (packet->tty[0]) { - strncpy(ttybuf, packet->tty, ARGV_SIZE); - av[ac++] = "-o"; - av[ac++] = ttybuf; - } - - /* tell the multiplexed backend to start on a certain port */ - if (MultiplexedBackends) { - sprintf(mbbuf, "-m %d", MultiplexedBackendPort); - av[ac++] = mbbuf; - } - /* Tell the backend the descriptor of the fe/be socket */ - sprintf(portbuf, "-P%d", portFd); - av[ac++] = portbuf; - - strNcpy(argbuf, packet->options, ARGV_SIZE); - strncat(argbuf, ExtraOptions, ARGV_SIZE); - argbuf[(2 * ARGV_SIZE)] = '\0'; - split_opts(av, &ac, argbuf); - - if (packet->database[0]) - strNcpy(dbbuf, packet->database, ARGV_SIZE); - else - strNcpy(dbbuf, packet->user, NAMEDATALEN-1); - av[ac++] = dbbuf; - - av[ac] = (char *) NULL; - - if (DebugLvl > 1) { - fprintf(stderr, "%s child[%ld]: execv(", - progname, (long)getpid()); - for (i = 0; i < ac; ++i) - fprintf(stderr, "%s, ", av[i]); - fprintf(stderr, ")\n"); - } - - return(execv(av[0], av)); + char execbuf[MAXPATHLEN]; + char portbuf[ARGV_SIZE]; + char mbbuf[ARGV_SIZE]; + char debugbuf[ARGV_SIZE]; + char ttybuf[ARGV_SIZE + 1]; + char argbuf[(2 * ARGV_SIZE) + 1]; + + /* + * each argument takes at least three chars, so we can't have more + * than ARGV_SIZE arguments in (2 * ARGV_SIZE) chars (i.e., + * packet->options plus ExtraOptions)... + */ + char *av[ARGV_SIZE]; + char dbbuf[ARGV_SIZE + 1]; + int ac = 0; + int i; + + strncpy(execbuf, Execfile, MAXPATHLEN - 1); + av[ac++] = execbuf; + + /* Tell the backend it is being called from the postmaster */ + av[ac++] = "-p"; + + /* + * Pass the requested debugging level along to the backend. We + * decrement by one; level one debugging in the postmaster traces + * postmaster connection activity, and levels two and higher are + * passed along to the backend. This allows us to watch only the + * postmaster or the postmaster and the backend. + */ + + if (DebugLvl > 1) + { + sprintf(debugbuf, "-d%d", DebugLvl); + av[ac++] = debugbuf; + } + else + av[ac++] = "-Q"; + + /* Pass the requested debugging output file */ + if (packet->tty[0]) + { + strncpy(ttybuf, packet->tty, ARGV_SIZE); + av[ac++] = "-o"; + av[ac++] = ttybuf; + } + + /* tell the multiplexed backend to start on a certain port */ + if (MultiplexedBackends) + { + sprintf(mbbuf, "-m %d", MultiplexedBackendPort); + av[ac++] = mbbuf; + } + /* Tell the backend the descriptor of the fe/be socket */ + sprintf(portbuf, "-P%d", portFd); + av[ac++] = portbuf; + + strNcpy(argbuf, packet->options, ARGV_SIZE); + strncat(argbuf, ExtraOptions, ARGV_SIZE); + argbuf[(2 * ARGV_SIZE)] = '\0'; + split_opts(av, &ac, argbuf); + + if (packet->database[0]) + strNcpy(dbbuf, packet->database, ARGV_SIZE); + else + strNcpy(dbbuf, packet->user, NAMEDATALEN - 1); + av[ac++] = dbbuf; + + av[ac] = (char *) NULL; + + if (DebugLvl > 1) + { + fprintf(stderr, "%s child[%ld]: execv(", + progname, (long) getpid()); + for (i = 0; i < ac; ++i) + fprintf(stderr, "%s, ", av[i]); + fprintf(stderr, ")\n"); + } + + return (execv(av[0], av)); } /* @@ -1153,34 +1259,31 @@ DoExec(StartupInfo *packet, int portFd) static void ExitPostmaster(int status) { - /* should cleanup shared memory and kill all backends */ - - /* - * Not sure of the semantics here. When the Postmaster dies, - * should the backends all be killed? probably not. - */ - if (ServerSock != INVALID_SOCK) - close(ServerSock); - exitpg(status); + /* should cleanup shared memory and kill all backends */ + + /* + * Not sure of the semantics here. When the Postmaster dies, should + * the backends all be killed? probably not. + */ + if (ServerSock != INVALID_SOCK) + close(ServerSock); + exitpg(status); } static void dumpstatus(SIGNAL_ARGS) { - Dlelem *curr = DLGetHead(PortList); - - while (curr) { - Port *port = DLE_VAL(curr); - - fprintf(stderr, "%s: dumpstatus:\n", progname); - fprintf(stderr, "\tsock %d: nBytes=%d, laddr=0x%lx, raddr=0x%lx\n", - port->sock, port->nBytes, - (long int) port->laddr.sin_addr.s_addr, - (long int) port->raddr.sin_addr.s_addr); - curr = DLGetSucc(curr); - } + Dlelem *curr = DLGetHead(PortList); + + while (curr) + { + Port *port = DLE_VAL(curr); + + fprintf(stderr, "%s: dumpstatus:\n", progname); + fprintf(stderr, "\tsock %d: nBytes=%d, laddr=0x%lx, raddr=0x%lx\n", + port->sock, port->nBytes, + (long int) port->laddr.sin_addr.s_addr, + (long int) port->raddr.sin_addr.s_addr); + curr = DLGetSucc(curr); + } } - - - - |