diff options
Diffstat (limited to 'src/backend/postmaster/postmaster.c')
-rw-r--r-- | src/backend/postmaster/postmaster.c | 1122 |
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 */ +} + + |