/*------------------------------------------------------------------------- * * 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.3 1996/07/23 02:23:47 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 */ #include #include #ifndef WIN32 #include #endif /* WIN32 */ #include #include /* for fd_set stuff */ #include /* for umask */ #include #include /* for MAXHOSTNAMELEN on most */ #ifdef WIN32 #include #include #define MAXINT INT_MAX #else #include /* for MAXHOSTNAMELEN on some */ # if defined(PORTNAME_BSD44_derived) || \ defined(PORTNAME_bsdi) || \ defined(PORTNAME_bsdi_2_1) # include # define MAXINT INT_MAX # else # include # endif /* !PORTNAME_BSD44_derived */ #include #endif /* WIN32 */ #include #include #include #if defined(PORTNAME_aix) #include #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 */ }