aboutsummaryrefslogtreecommitdiff
path: root/src/backend/postmaster/postmaster.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/postmaster/postmaster.c')
-rw-r--r--src/backend/postmaster/postmaster.c1122
1 files changed, 1122 insertions, 0 deletions
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
new file mode 100644
index 00000000000..fe09e16e808
--- /dev/null
+++ b/src/backend/postmaster/postmaster.c
@@ -0,0 +1,1122 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.1.1.1 1996/07/09 06:21:49 scrappy 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.
+ *
+ * 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.
+ *
+ * Garbage Collection:
+ * The Postmaster cleans up after backends if they have an emergency
+ * exit and/or core dump.
+ *
+ * Communication:
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "libpq/pqsignal.h" /* substitute for <signal.h> */
+#include <string.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif /* WIN32 */
+#include <ctype.h>
+#include <sys/types.h> /* for fd_set stuff */
+#include <sys/stat.h> /* for umask */
+#include <sys/time.h>
+#include <sys/param.h> /* for MAXHOSTNAMELEN on most */
+#ifdef WIN32
+#include <winsock.h>
+#include <limits.h>
+#define MAXINT INT_MAX
+#else
+#include <netdb.h> /* for MAXHOSTNAMELEN on some */
+# if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_bsdi)
+# include <machine/limits.h>
+# define MAXINT INT_MAX
+# else
+# include <values.h>
+# endif /* !PORTNAME_BSD44_derived */
+#include <sys/wait.h>
+#endif /* WIN32 */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#if defined(PORTNAME_aix)
+#include <sys/select.h>
+#endif /* PORTNAME_aix */
+
+#include "storage/ipc.h"
+#include "libpq/libpq.h"
+#include "libpq/auth.h"
+#include "libpq/pqcomm.h"
+#include "miscadmin.h"
+#include "lib/dllist.h"
+#include "utils/mcxt.h"
+#include "storage/proc.h"
+#include "utils/elog.h"
+
+#ifdef DBX_VERSION
+#define FORK() (0)
+#else
+#if defined(PORTNAME_irix5)
+/* IRIX 5 does not have vfork() */
+#define FORK() fork()
+#else
+#define FORK() vfork()
+#endif
+#endif
+
+/*
+ * Info for garbage collection. Whenever a process dies, the Postmaster
+ * 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;
+
+/* list of active backends. For garbage collection only now. */
+
+static Dllist* BackendList;
+
+/* list of ports associated with still open, but incomplete connections */
+static Dllist* PortList;
+
+static short PostPortName = -1;
+static short ActiveBackends = FALSE;
+static int NextBackendId = MAXINT; /* XXX why? */
+static char *progname = (char *) NULL;
+
+char *DataDir = (char *) NULL;
+
+/*
+ * Default Values
+ */
+static char Execfile[MAXPATHLEN] = "";
+
+static int ServerSock = INVALID_SOCK; /* stream socket server */
+
+/*
+ * Set by the -o option
+ */
+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
+ * 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 MultiplexedBackends = 0;
+static int MultiplexedBackendPort;
+
+#ifdef HBA
+static int useHostBasedAuth = 1;
+#else
+static int useHostBasedAuth = 0;
+#endif
+
+/*
+ * postmaster.c - function prototypes
+ */
+static void pmdaemonize(void);
+static int ConnStartup(Port *port);
+static int ConnCreate(int serverFd, int *newFdP);
+static void reset_shared(short port);
+#if defined(PORTNAME_linux)
+static void pmdie(int);
+static void reaper(int);
+static void dumpstatus(int);
+#else
+static void pmdie(void);
+static void reaper(void);
+static void dumpstatus();
+#endif
+static void CleanupProc(int pid, int exitstatus);
+static int DoExec(StartupInfo *packet, int portFd);
+static void ExitPostmaster(int status);
+static void usage();
+static void checkDataDir();
+
+int ServerLoop(void);
+int BackendStartup(StartupInfo *packet, Port *port, int *pidPtr);
+
+extern char *optarg;
+extern int optind, opterr;
+
+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];
+#ifdef WIN32
+ WSADATA WSAData;
+#endif /* WIN32 */
+
+ progname = argv[0];
+
+ /* for security, no dir or file created can be group or other accessible */
+ (void) umask((mode_t) 0077);
+
+ if (!(hostName = getenv("PGHOST"))) {
+ if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
+ (void) strcpy(hostbuf, "localhost");
+ hostName = hostbuf;
+ }
+
+ opterr = 0;
+ while ((opt = getopt(argc, argv, "a:B:b:D:dmM:no: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);
+ (void) strcat(ExtraOptions, " -B ");
+ (void) 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.
+ */
+ (void) strcat(ExtraOptions, " ");
+ (void) 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();
+
+ IsPostmaster = true;
+
+ if (!DataDir)
+ DataDir = GetPGData();
+
+ /*
+ * check whether the data directory exists. Passing this test doesn't
+ * gaurantee we are accessing the right data base but is a first barrier
+ * to site administrators who starts up the postmaster without realizing
+ * it cannot access the data base.
+ */
+ checkDataDir();
+
+ if (!Execfile[0] && FindBackend(Execfile, argv[0]) < 0) {
+ fprintf(stderr, "%s: could not find backend to execute...\n",
+ argv[0]);
+ exit(1);
+ }
+
+
+#ifdef WIN32
+ if ((status = WSAStartup(MAKEWORD(1,1), &WSAData)) == 0)
+ (void) printf("%s\nInitializing WinSock: %s\n", WSAData.szDescription, WSAData.szSystemStatus);
+ else
+ {
+ fprintf(stderr, "Error initializing WinSock: %d is the err", status);
+ exit(1);
+ }
+ _nt_init();
+ _nt_attach();
+#endif /* WIN32 */
+
+ 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();
+
+ signal(SIGINT, pmdie);
+#ifndef WIN32
+ signal(SIGCHLD, reaper);
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGHUP, pmdie);
+ signal(SIGTERM, pmdie);
+ signal(SIGCONT, dumpstatus);
+#endif /* WIN32 */
+
+
+ status = ServerLoop();
+
+ ExitPostmaster(status != STATUS_OK);
+ return 0; /* not reached */
+}
+
+static void
+pmdaemonize()
+{
+ int i;
+
+ if (fork())
+ exit(0);
+
+ if (setsid() < 0) {
+ fprintf(stderr, "%s: ", progname);
+ perror("cannot disassociate from controlling TTY");
+ exit(1);
+ }
+ i = open(NULL_DEV, O_RDWR);
+ (void) dup2(i, 0);
+ (void) dup2(i, 1);
+ (void) dup2(i, 2);
+ (void) close(i);
+}
+
+static void
+usage(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);
+}
+
+int
+ServerLoop()
+{
+ int serverFd = ServerSock;
+ fd_set rmask, basemask;
+ int nSockets, nSelected, status, newFd;
+ Dlelem *prev, *curr;
+/* int orgsigmask = sigblock(0); */
+ sigset_t oldsigmask, newsigmask;
+
+ nSockets = ServerSock + 1;
+ FD_ZERO(&basemask);
+ FD_SET(ServerSock, &basemask);
+
+ sigprocmask(0,0,&oldsigmask);
+ sigemptyset(&newsigmask);
+ sigaddset(&newsigmask,SIGCHLD);
+ for (;;) {
+/* sigsetmask(orgsigmask); */
+ sigprocmask(SIG_SETMASK,&oldsigmask,0);
+ 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.)
+ */
+ sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask);
+/* sigblock(sigmask(SIGCHLD)); */ /* XXX[TRH] portability */
+
+ }
+ 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:
+ ConnStartup(port);
+ 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);
+ prev = DLGetPred(curr);
+ DLRemove(curr);
+ DLFreeElem(curr);
+ curr = 0;
+ }
+ curr = DLGetSucc(curr);
+ }
+ Assert(nSelected == 0);
+ }
+}
+
+static int
+ConnStartup(Port *port) /* receiving port */
+{
+ MsgType msgType;
+ char namebuf[NAMEDATALEN + 1];
+/* StartupInfo *sp;*/
+ int pid;
+ PacketBuf *p;
+/* sp = PacketBuf2StartupInfo(&port->buf);*/
+ 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 = ntohl(port->buf.msgtype);
+
+ (void) strncpy(namebuf, sp.user, NAMEDATALEN);
+ namebuf[NAMEDATALEN] = '\0';
+ if (!namebuf[0]) {
+ fprintf(stderr, "%s: ConnStartup: no user name specified\n",
+ progname);
+ return(STATUS_ERROR);
+ }
+
+ if (msgType == STARTUP_MSG && useHostBasedAuth)
+ msgType = STARTUP_HBA_MSG;
+ if (be_recvauth(msgType, port, namebuf,&sp) != STATUS_OK) {
+ fprintf(stderr, "%s: ConnStartup: authentication failed\n",
+ progname);
+ return(STATUS_ERROR);
+ }
+
+ if (BackendStartup(&sp, port, &pid) != STATUS_OK) {
+ fprintf(stderr, "%s: ConnStartup: couldn't start backend\n",
+ progname);
+ return(STATUS_ERROR);
+ }
+
+ return(STATUS_OK);
+}
+
+/*
+ * ConnCreate -- create a local connection data structure
+ */
+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);
+}
+
+/*
+ * reset_shared -- reset shared memory and semaphores
+ */
+static void
+reset_shared(short port)
+{
+ IPCKey key;
+
+ key = SystemPortAddressCreateIPCKey((SystemPortAddress) port);
+ CreateSharedMemoryAndSemaphores(key);
+ ActiveBackends = FALSE;
+}
+
+/*
+ * pmdie -- signal handler for cleaning up after a kill signal.
+ */
+static void
+#if defined(PORTNAME_linux)
+pmdie(int i)
+#else
+pmdie()
+#endif
+{
+ exitpg(0);
+}
+
+/*
+ * Reaper -- signal handler to cleanup after a backend (child) dies.
+ */
+static void
+#if defined(PORTNAME_linux)
+reaper(int i)
+#else
+reaper()
+#endif
+{
+ int status; /* backend exit status */
+ int pid; /* process id of dead backend */
+
+ if (DebugLvl)
+ fprintf(stderr, "%s: reaping dead processes...\n",
+ progname);
+#ifndef WIN32
+ while((pid = waitpid(-1, &status, WNOHANG)) > 0)
+ CleanupProc(pid, status);
+#endif /* WIN32 */
+}
+
+/*
+ * CleanupProc -- cleanup after terminated backend.
+ *
+ * Remove all local state associated with backend.
+ *
+ * Dillon's note: should log child's exit status in the system log.
+ */
+static void
+CleanupProc(int pid,
+ 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);
+ 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
+ * the command line), then we send a SIGSTOP so that we can
+ * collect core dumps from all backends by hand.
+ * -----------------
+ */
+#ifndef WIN32
+ 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);
+ (void) kill(bp->pid, sig);
+ }
+#endif /* WIN32 */
+ ProcRemove(bp->pid);
+
+ prev = DLGetPred(curr);
+ DLRemove(curr);
+ DLFreeElem(curr);
+ if (!prev) { /* removed head */
+ curr = DLGetHead(BackendList);
+ continue;
+ }
+ curr = DLGetSucc(curr);
+ }
+ /*
+ * -------------
+ * 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.
+ *
+ */
+int
+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], 2*ARGV_SIZE,0);
+ }
+ /*
+ * 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");
+ }
+
+#ifndef WIN32
+ 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);
+ }
+#else
+ pid = DoExec(packet, port->sock);
+ if (pid == FALSE) {
+ fprintf(stderr, "%s: BackendStartup: CreateProcess failed\n",
+ progname);
+ return(STATUS_ERROR);
+ }
+#endif /* WIN32 */
+
+ 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);
+}
+
+/*
+ * split_opts -- destructively load a string into an argv array
+ *
+ * Since no current POSTGRES arguments require any quoting characters,
+ * we can use the simple-minded tactic of assuming each set of space-
+ * delimited characters is a separate argv element.
+ *
+ * If you don't like that, well, we *used* to pass the whole option string
+ * as ONE argument to execl(), which was even less intelligent...
+ */
+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;
+}
+
+/*
+ * DoExec -- set up the argument list and perform an execv system call
+ *
+ * Tries fairly hard not to dork with anything that isn't automatically
+ * allocated so we don't do anything weird to the postmaster when it gets
+ * 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.
+ */
+static int
+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;
+#ifdef WIN32
+ char win32_args[(2 * ARGV_SIZE) + 1];
+ PROCESS_INFORMATION piProcInfo;
+ STARTUPINFO siStartInfo;
+ BOOL fSuccess;
+#endif /* WIN32 */
+
+ (void) strncpy(execbuf, Execfile, MAXPATHLEN);
+ execbuf[MAXPATHLEN - 1] = '\0';
+ 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) {
+ (void) sprintf(debugbuf, "-d%d", DebugLvl - 1);
+ av[ac++] = debugbuf;
+ }
+ else
+ av[ac++] = "-Q";
+
+ /* Pass the requested debugging output file */
+ if (packet->tty[0]) {
+ (void) strncpy(ttybuf, packet->tty, ARGV_SIZE);
+ av[ac++] = "-o";
+#ifdef WIN32
+ /* BIG HACK - The front end is passing "/dev/null" here which
+ ** causes new backends to fail. So, as a very special case,
+ ** use a real NT filename.
+ */
+ av[ac++] = "CON";
+#else
+ av[ac++] = ttybuf;
+#endif /* WIN32 */
+
+ }
+
+ /* 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 */
+ (void) sprintf(portbuf, "-P%d", portFd);
+ av[ac++] = portbuf;
+
+ (void) strncpy(argbuf, packet->options, ARGV_SIZE);
+ argbuf[ARGV_SIZE] = '\0';
+ (void) strncat(argbuf, ExtraOptions, ARGV_SIZE);
+ argbuf[(2 * ARGV_SIZE) + 1] = '\0';
+ split_opts(av, &ac, argbuf);
+
+ if (packet->database[0])
+ (void) strncpy(dbbuf, packet->database, ARGV_SIZE);
+ else
+ (void) strncpy(dbbuf, packet->user, NAMEDATALEN);
+ dbbuf[ARGV_SIZE] = '\0';
+ av[ac++] = dbbuf;
+
+ av[ac] = (char *) NULL;
+
+ if (DebugLvl > 1) {
+ fprintf(stderr, "%s child[%d]: execv(",
+ progname, getpid());
+ for (i = 0; i < ac; ++i)
+ fprintf(stderr, "%s, ", av[i]);
+ fprintf(stderr, ")\n");
+ }
+
+#ifndef WIN32
+ return(execv(av[0], av));
+#else
+
+ /* Copy all the arguments into one char array */
+ win32_args[0] = '\0';
+ for (i = 0; i < ac; i++)
+ {
+ strcat(win32_args, av[i]);
+ strcat(win32_args, " ");
+ }
+
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.lpReserved = NULL;
+ siStartInfo.lpDesktop = NULL;
+ siStartInfo.lpTitle = NULL;
+ siStartInfo.lpReserved2 = NULL;
+ siStartInfo.cbReserved2 = 0;
+ siStartInfo.dwFlags = 0;
+
+
+ fSuccess = CreateProcess(progname, win32_args, NULL, NULL,
+ TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
+ if (fSuccess)
+ {
+ /* The parent process doesn't need the handles */
+ CloseHandle(piProcInfo.hThread);
+ CloseHandle(piProcInfo.hProcess);
+ return (piProcInfo.dwProcessId);
+ }
+ else
+ return (FALSE);
+#endif /* WIN32 */
+}
+
+/*
+ * ExitPostmaster -- cleanup
+ */
+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);
+}
+
+static void
+#if defined(PORTNAME_linux)
+dumpstatus(int i)
+#else
+dumpstatus()
+#endif
+{
+ 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%x, raddr=0x%x\n",
+ port->sock, port->nBytes,
+ port->laddr,
+ port->raddr);
+ curr = DLGetSucc(curr);
+ }
+}
+
+static void
+checkDataDir()
+{
+ char path[MAXPATHLEN];
+ FILE *fp;
+
+ sprintf(path, "%s%cbase%ctemplate1%cpg_class", DataDir, SEP_CHAR, SEP_CHAR,
+ SEP_CHAR);
+ if ((fp=fopen(path, "r")) == NULL) {
+ fprintf(stderr, "%s: data base not found in directory \"%s\"\n",
+ progname, DataDir);
+ exit(2);
+ }
+ fclose(fp);
+
+#ifndef WIN32
+ if (!ValidPgVersion(DataDir)) {
+ fprintf(stderr, "%s: data base in \"%s\" is of a different version.\n",
+ progname, DataDir);
+ exit(2);
+ }
+#endif /* WIN32 */
+}
+
+