/*------------------------------------------------------------------------- * * 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.129 1999/12/04 08:23:43 ishii 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 #include #include #include #include #include #include #include #include #include #include #include #include "postgres.h" /* moved here to prevent double define */ #ifdef HAVE_NETDB_H #include #endif #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 256 #endif #ifdef HAVE_LIMITS_H #include #else #ifdef HAVE_VALUES_H #include #endif #endif #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_GETOPT_H #include "getopt.h" #endif #ifndef HAVE_GETHOSTNAME #include "port-protos.h" #endif #include "commands/async.h" #include "lib/dllist.h" #include "libpq/auth.h" #include "libpq/crypt.h" #include "libpq/libpq.h" #include "libpq/pqcomm.h" #include "libpq/pqsignal.h" #include "miscadmin.h" #include "nodes/nodes.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/proc.h" #include "access/xlog.h" #include "tcop/tcopprot.h" #include "utils/trace.h" #include "version.h" /* * "postmaster.pid" is a file containing postmaster's pid, being * created uder $PGDATA upon postmaster's starting up. When postmaster * shuts down, it will be unlinked. The purpose of the file includes: * * (1) supplying neccessary information to stop/restart postmaster * (2) preventing another postmaster process starting while it has * already started */ #define PIDFNAME "postmaster.pid" /* * "postmaster.opts" is a file containing options for postmaser. * pg_ctl will use it to restart postmaster. */ #define OPTSFNAME "postmaster.opts" #if !defined(MAXINT) #define MAXINT INT_MAX #endif #define INVALID_SOCK (-1) #define ARGV_SIZE 64 #ifdef HAVE_SIGPROCMASK sigset_t UnBlockSig, BlockSig; #else int UnBlockSig, BlockSig; #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 */ long cancel_key; /* cancel key for cancels for this backend */ } Backend; Port *MyBackendPort = NULL; /* 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 unsigned short PostPortName = 0; /* * This is a boolean indicating that there is at least one backend that * is accessing the current shared memory and semaphores. Between the * time that we start up, or throw away shared memory segments and start * over, and the time we generate the next backend (because we received a * connection request), it is false. Other times, it is true. */ static short shmem_seq = 0; /* * This is a sequence number that indicates how many times we've had to * throw away the shared memory and start over because we doubted its * integrity. It starts off at zero and is incremented every time we * start over. We use this to ensure that we use a new IPC shared memory * key for the new shared memory segment in case the old segment isn't * entirely gone yet. * * The sequence actually cycles back to 0 after 9, so pathologically there * could be an IPC failure if 10 sets of backends are all stuck and won't * release IPC resources. */ static IpcMemoryKey ipc_key; /* * This is the base IPC shared memory key. Other keys are generated by * adding to this. */ static int MaxBackends = DEF_MAXBACKENDS; /* * MaxBackends is the actual limit on the number of backends we will * start. The default is established by configure, but it can be * readjusted from 1..MAXBACKENDS with the postmaster -N switch. Note * that a larger MaxBackends value will increase the size of the shared * memory area as well as cause the postmaster to grab more kernel * semaphores, even if you never actually use that many backends. */ static int NextBackendTag = MAXINT; /* XXX why count down not up? */ static char *progname = (char *) NULL; static char **real_argv; static int real_argc; static time_t tnow; /* * Default Values */ static char Execfile[MAXPGPATH]; static int ServerSock_INET = INVALID_SOCK; /* stream socket server */ #ifndef __CYGWIN32__ static int ServerSock_UNIX = INVALID_SOCK; /* stream socket server */ #endif #ifdef USE_SSL static SSL_CTX *SSL_context = NULL; /* Global SSL context */ #endif /* * Set by the -o option */ static char ExtraOptions[MAXPGPATH]; /* * 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 bool Reinit = true; static int SendStop = false; static bool NetServer = false; /* if not zero, postmaster listen for * non-local connections */ #ifdef USE_SSL static bool SecureNetServer = false; /* if not zero, postmaster listens for only SSL * non-local connections */ #endif static pid_t StartupPID = 0, ShutdownPID = 0; #define NoShutdown 0 #define SmartShutdown 1 #define FastShutdown 2 static int Shutdown = NoShutdown; static bool FatalError = false; /* * State for assigning random salts and cancel keys. * Also, the global MyCancelKey passes the cancel key assigned to a given * backend from the postmaster to that backend (via fork). */ static unsigned int random_seed = 0; extern char *optarg; extern int optind, opterr; /* * postmaster.c - function prototypes */ static void pmdaemonize(char *extraoptions); static Port *ConnCreate(int serverFd); static void ConnFree(Port *port); static void reset_shared(unsigned 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 DoBackend(Port *port); static void ExitPostmaster(int status); static void usage(const char *); static int ServerLoop(void); static int BackendStartup(Port *port); static int readStartupPacket(void *arg, PacketLen len, void *pkt); static int processCancelRequest(Port *port, PacketLen len, void *pkt); static int initMasks(fd_set *rmask, fd_set *wmask); static long PostmasterRandom(void); static void RandomSalt(char *salt); static void SignalChildren(SIGNAL_ARGS); static int CountChildren(void); static int SetPidFile(pid_t pid, char *progname, int port, char *datadir, int assert, int nbuf, char *execfile, int debuglvl, int netserver, #ifdef USE_SSL int securenetserver, #endif int maxbackends, int reinit, int silent, int sendstop, char *extraoptions); extern int BootstrapMain(int argc, char *argv[]); static pid_t SSDataBase(bool startup); #define StartupDataBase() SSDataBase(true) #define ShutdownDataBase() SSDataBase(false) #ifdef USE_SSL static void InitSSL(void); #endif #ifdef CYR_RECODE void GetCharSetByHost(char *, int, char *); #endif #ifdef USE_ASSERT_CHECKING int assert_enabled = 1; #endif static void 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[MAXPGPATH]; FILE *fp; snprintf(path, sizeof(path), "%s%cbase%ctemplate1%cpg_class", DataDir, SEP_CHAR, SEP_CHAR, SEP_CHAR); #ifndef __CYGWIN32__ fp = AllocateFile(path, "r"); #else fp = AllocateFile(path, "rb"); #endif 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 */ FreeFile(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; } } } int PostmasterMain(int argc, char *argv[]) { extern int NBuffers; /* from buffer/bufmgr.c */ int opt; char *hostName; int status; int silentflag = 0; bool DataDirOK; /* We have a usable PGDATA value */ char hostbuf[MAXHOSTNAMELEN]; int nonblank_argc; char original_extraoptions[MAXPGPATH]; *original_extraoptions = '\0'; /* * We need three params so we can display status. If we don't get * them from the user, let's make them ourselves. */ if (argc < 5) { int i; char *new_argv[6]; for (i = 0; i < argc; i++) new_argv[i] = argv[i]; for (; i < 5; i++) new_argv[i] = ""; new_argv[5] = NULL; if (!Execfile[0] && FindExec(Execfile, argv[0], "postmaster") < 0) { fprintf(stderr, "%s: could not find postmaster to execute...\n", argv[0]); exit(1); } new_argv[0] = Execfile; execv(new_argv[0], new_argv); /* How did we get here, error! */ perror(new_argv[0]); fprintf(stderr, "PostmasterMain execv failed on %s\n", argv[0]); exit(1); } progname = argv[0]; real_argv = argv; real_argc = argc; /* * don't process any dummy args we placed at the end for status * display */ for (nonblank_argc = argc; nonblank_argc > 0; nonblank_argc--) if (argv[nonblank_argc - 1] != NULL && argv[nonblank_argc - 1][0] != '\0') break; /* * 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; } MyProcPid = getpid(); DataDir = getenv("PGDATA"); /* default value */ opterr = 0; while ((opt = getopt(nonblank_argc, argv, "A:a:B:b:D:d:ilm:MN:no:p:Ss")) != EOF) { switch (opt) { case 'A': #ifndef USE_ASSERT_CHECKING fprintf(stderr, "Assert checking is not enabled\n"); #else /* * Pass this option also to each backend. */ assert_enabled = atoi(optarg); strcat(ExtraOptions, " -A "); strcat(ExtraOptions, optarg); #endif break; case 'a': /* Can no longer set authentication method. */ 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 = atoi(optarg); strcat(ExtraOptions, " -B "); strcat(ExtraOptions, optarg); break; case 'b': /* Set the backend executable file to use. */ if (!ValidateBinary(optarg)) StrNCpy(Execfile, optarg, MAXPGPATH); 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. */ DebugLvl = atoi(optarg); pg_options[TRACE_VERBOSE] = DebugLvl; break; case 'i': NetServer = true; break; #ifdef USE_SSL case 'l': SecureNetServer = true; break; #endif case 'm': /* Multiplexed backends no longer supported. */ 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': /* * The max number of backends to start. Can't set to less * than 1 or more than compiled-in limit. */ MaxBackends = atoi(optarg); if (MaxBackends < 1) MaxBackends = 1; if (MaxBackends > MAXBACKENDS) MaxBackends = MAXBACKENDS; break; case 'n': /* Don't reinit shared mem after abnormal exit */ Reinit = false; break; case 'o': /* * Other options to pass to the backend on the command * line -- useful only for debugging. */ strcat(ExtraOptions, " "); strcat(ExtraOptions, optarg); strcpy(original_extraoptions, optarg); break; case 'p': /* Set PGPORT by hand. */ PostPortName = (unsigned 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 = true; break; default: /* usage() never returns */ usage(progname); break; } } /* * Select default values for switches where needed */ if (PostPortName == 0) PostPortName = (unsigned short)pq_getport(); /* * Check for invalid combinations of switches */ if (NBuffers < 2 * MaxBackends || NBuffers < 16) { /* Do not accept -B so small that backends are likely to starve for * lack of buffers. The specific choices here are somewhat arbitrary. */ fprintf(stderr, "%s: -B must be at least twice -N and at least 16.\n", progname); exit(1); } checkDataDir(DataDir, &DataDirOK); /* issues error messages */ if (!DataDirOK) { fprintf(stderr, "No data directory -- can't proceed.\n"); exit(2); } if (!Execfile[0] && FindExec(Execfile, argv[0], "postgres") < 0) { fprintf(stderr, "%s: could not find backend to execute...\n", argv[0]); exit(1); } #ifdef USE_SSL if (!NetServer && SecureNetServer) { fprintf(stderr, "For SSL, you must enable TCP/IP connections.\n", argv[0]); exit(1); } InitSSL(); #endif if (NetServer) { status = StreamServerPort(hostName, PostPortName, &ServerSock_INET); if (status != STATUS_OK) { fprintf(stderr, "%s: cannot create INET stream port\n", progname); exit(1); } } #ifndef __CYGWIN32__ status = StreamServerPort(NULL, PostPortName, &ServerSock_UNIX); if (status != STATUS_OK) { fprintf(stderr, "%s: cannot create UNIX stream port\n", progname); exit(1); } #endif /* 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(original_extraoptions); } else { /* * create pid file. if the file has already existed, exits. */ if (SetPidFile( getpid(), /* postmaster process id */ progname, /* postmaster executable file */ PostPortName, /* port number */ DataDir, /* PGDATA */ assert_enabled, /* whether -A is specified or not */ NBuffers, /* -B: number of shared buffers */ Execfile, /* -b: postgres executable file */ DebugLvl, /* -d: debug level */ NetServer, /* -i: accept connection from INET */ #ifdef USE_SSL SecureNetServer, /* -l: use SSL */ #endif MaxBackends, /* -N: max number of backends */ Reinit, /* -n: reinit shared mem after failure */ silentflag, /* -S: detach tty */ SendStop, /* -s: send SIGSTOP */ original_extraoptions /* options for backend */ ) ) { ExitPostmaster(1); return 0; /* not reached */ } } /* * Set up signal handlers for the postmaster process. */ PG_INITMASK(); PG_SETMASK(&BlockSig); pqsignal(SIGHUP, pmdie); /* send SIGHUP, don't die */ pqsignal(SIGINT, pmdie); /* send SIGTERM and ShutdownDataBase */ pqsignal(SIGQUIT, pmdie); /* send SIGUSR1 and die */ pqsignal(SIGTERM, pmdie); /* wait for children and ShutdownDataBase */ pqsignal(SIGALRM, SIG_IGN); /* ignored */ pqsignal(SIGPIPE, SIG_IGN); /* ignored */ pqsignal(SIGUSR1, SIG_IGN); /* ignored */ pqsignal(SIGUSR2, pmdie); /* send SIGUSR2, don't die */ pqsignal(SIGCHLD, reaper); /* handle child termination */ pqsignal(SIGTTIN, SIG_IGN); /* ignored */ pqsignal(SIGTTOU, SIG_IGN); /* ignored */ pqsignal(SIGWINCH, dumpstatus); /* dump port status */ StartupPID = StartupDataBase(); status = ServerLoop(); ExitPostmaster(status != STATUS_OK); return 0; /* not reached */ } static void pmdaemonize(char *extraoptions) { int i; pid_t pid; pid = fork(); if (pid == -1) { perror("Failed to fork postmaster"); ExitPostmaster(1); return; /* not reached */ } else if (pid) { /* parent */ /* * create pid file. if the file has already existed, exits. */ if (SetPidFile( pid, /* postmaster process id */ progname, /* postmaster executable file */ PostPortName, /* port number */ DataDir, /* PGDATA */ assert_enabled, /* whether -A is specified or not */ NBuffers, /* -B: number of shared buffers */ Execfile, /* -b: postgres executable file */ DebugLvl, /* -d: debug level */ NetServer, /* -i: accept connection from INET */ #ifdef USE_SSL SecureNetServer, /* -l: use SSL */ #endif MaxBackends, /* -N: max number of backends */ Reinit, /* -n: reinit shared mem after failure */ 1, /* -S: detach tty */ SendStop, /* -s: send SIGSTOP */ extraoptions /* options for backend */ ) ) { /* * Failed to create pid file. kill the child and * exit now. */ kill(pid, SIGTERM); ExitPostmaster(1); return; /* not reached */ } _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); } #endif #ifndef __CYGWIN32__ i = open(NULL_DEV, O_RDWR); #else i = open(NULL_DEV, O_RDWR | O_BINARY); #endif dup2(i, 0); dup2(i, 1); dup2(i, 2); close(i); } static void usage(const char *progname) { fprintf(stderr, "usage: %s [options]\n", progname); #ifdef USE_ASSERT_CHECKING fprintf(stderr, "\t-A [1|0]\tenable/disable runtime assert checking\n"); #endif fprintf(stderr, "\t-B nbufs\tset number of shared buffers\n"); fprintf(stderr, "\t-D datadir\tset data directory\n"); fprintf(stderr, "\t-S \t\tsilent mode (disassociate from tty)\n"); fprintf(stderr, "\t-a system\tuse this authentication system\n"); fprintf(stderr, "\t-b backend\tuse a specific backend server executable\n"); fprintf(stderr, "\t-d [1-5]\tset debugging level\n"); fprintf(stderr, "\t-i \t\tlisten on TCP/IP sockets as well as Unix domain socket\n"); #ifdef USE_SSL fprintf(stderr," \t-l \t\tfor TCP/IP sockets, listen only on SSL connections\n"); #endif fprintf(stderr, "\t-N nprocs\tset max number of backends (1..%d, default %d)\n", MAXBACKENDS, DEF_MAXBACKENDS); 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\tspecify port for postmaster to listen on\n"); fprintf(stderr, "\t-s \t\tsend SIGSTOP to all backend servers if one dies\n"); exit(1); } static int ServerLoop(void) { fd_set readmask, writemask; int nSockets; Dlelem *curr; struct timeval now, later; struct timezone tz; gettimeofday(&now, &tz); nSockets = initMasks(&readmask, &writemask); for (;;) { Port *port; fd_set rmask, wmask; #ifdef USE_SSL int no_select = 0; #endif memmove((char *) &rmask, (char *) &readmask, sizeof(fd_set)); memmove((char *) &wmask, (char *) &writemask, sizeof(fd_set)); #ifdef USE_SSL for (curr = DLGetHead(PortList); curr; curr = DLGetSucc(curr)) { if (((Port *)DLE_VAL(curr))->ssl && SSL_pending(((Port *)DLE_VAL(curr))->ssl) > 0) { no_select = 1; break; } } PG_SETMASK(&UnBlockSig); if (no_select) FD_ZERO(&rmask); /* So we don't accept() anything below */ else #else PG_SETMASK(&UnBlockSig); #endif if (select(nSockets, &rmask, &wmask, (fd_set *) NULL, (struct timeval *) NULL) < 0) { if (errno == EINTR) continue; fprintf(stderr, "%s: ServerLoop: select failed: %s\n", progname, strerror(errno)); return STATUS_ERROR; } /* * Select a random seed at the time of first receiving a request. */ while (random_seed == 0) { gettimeofday(&later, &tz); /* * We are not sure how much precision is in tv_usec, so we * swap the nibbles of 'later' and XOR them with 'now'. On the * off chance that the result is 0, we loop until it isn't. */ random_seed = now.tv_usec ^ ((later.tv_usec << 16) | ((later.tv_usec >> 16) & 0xffff)); } /* * Block all signals */ PG_SETMASK(&BlockSig); /* new connection pending on our well-known port's socket */ #ifndef __CYGWIN32__ if (ServerSock_UNIX != INVALID_SOCK && FD_ISSET(ServerSock_UNIX, &rmask) && (port = ConnCreate(ServerSock_UNIX)) != NULL) { PacketReceiveSetup(&port->pktInfo, readStartupPacket, (void *) port); } #endif if (ServerSock_INET != INVALID_SOCK && FD_ISSET(ServerSock_INET, &rmask) && (port = ConnCreate(ServerSock_INET)) != NULL) { PacketReceiveSetup(&port->pktInfo, readStartupPacket, (void *) port); } /* Build up new masks for select(). */ nSockets = initMasks(&readmask, &writemask); curr = DLGetHead(PortList); while (curr) { Port *port = (Port *) DLE_VAL(curr); int status = STATUS_OK; Dlelem *next; int readyread = 0; #ifdef USE_SSL if (port->ssl) { if (SSL_pending(port->ssl) || FD_ISSET(port->sock, &rmask)) readyread = 1; } else #endif if (FD_ISSET(port->sock, &rmask)) readyread = 1; if (readyread) { if (DebugLvl > 1) fprintf(stderr, "%s: ServerLoop:\t\thandling reading %d\n", progname, port->sock); if (PacketReceiveFragment(port) != STATUS_OK) status = STATUS_ERROR; } if (FD_ISSET(port->sock, &wmask)) { if (DebugLvl > 1) fprintf(stderr, "%s: ServerLoop:\t\thandling writing %d\n", progname, port->sock); if (PacketSendFragment(port) != STATUS_OK) status = STATUS_ERROR; } /* Get this before the connection might be closed. */ next = DLGetSucc(curr); /* * If there is no error and no outstanding data transfer going * on, then the authentication handshake must be complete to * the postmaster's satisfaction. So, start the backend. */ if (status == STATUS_OK && port->pktInfo.state == Idle) { /* * Can't start backend if max backend count is exceeded. * * The same when shutdowning data base. */ if (Shutdown > NoShutdown) PacketSendError(&port->pktInfo, "The Data Base System is shutting down"); else if (StartupPID) PacketSendError(&port->pktInfo, "The Data Base System is starting up"); else if (FatalError) PacketSendError(&port->pktInfo, "The Data Base System is in recovery mode"); else if (CountChildren() >= MaxBackends) PacketSendError(&port->pktInfo, "Sorry, too many clients already"); else { /* * If the backend start fails then keep the connection * open to report it. Otherwise, pretend there is an * error to close the connection which will now be * managed by the backend. */ if (BackendStartup(port) != STATUS_OK) PacketSendError(&port->pktInfo, "Backend startup failed"); else status = STATUS_ERROR; } } /* Close the connection if required. */ if (status != STATUS_OK) { StreamClose(port->sock); DLRemove(curr); ConnFree(port); DLFreeElem(curr); } else { /* Set the masks for this connection. */ if (nSockets <= port->sock) nSockets = port->sock + 1; if (port->pktInfo.state == WritingPacket) FD_SET(port->sock, &writemask); else FD_SET(port->sock, &readmask); } curr = next; } } } /* * Initialise the read and write masks for select() for the well-known ports * we are listening on. Return the number of sockets to listen on. */ static int initMasks(fd_set *rmask, fd_set *wmask) { int nsocks = -1; FD_ZERO(rmask); FD_ZERO(wmask); #ifndef __CYGWIN32__ if (ServerSock_UNIX != INVALID_SOCK) { FD_SET(ServerSock_UNIX, rmask); if (ServerSock_UNIX > nsocks) nsocks = ServerSock_UNIX; } #endif if (ServerSock_INET != INVALID_SOCK) { FD_SET(ServerSock_INET, rmask); if (ServerSock_INET > nsocks) nsocks = ServerSock_INET; } return nsocks + 1; } /* * Called when the startup packet has been read. */ static int readStartupPacket(void *arg, PacketLen len, void *pkt) { Port *port; StartupPacket *si; port = (Port *) arg; si = (StartupPacket *) pkt; /* * The first field is either a protocol version number or a special * request code. */ port->proto = ntohl(si->protoVersion); if (port->proto == CANCEL_REQUEST_CODE) return processCancelRequest(port, len, pkt); if (port->proto == NEGOTIATE_SSL_CODE) { char SSLok; #ifdef USE_SSL SSLok = 'S'; /* Support for SSL */ #else SSLok = 'N'; /* No support for SSL */ #endif if (send(port->sock, &SSLok, 1, 0) != 1) { perror("Failed to send SSL negotiation response"); return STATUS_ERROR; /* Close connection */ } #ifdef USE_SSL if (!(port->ssl = SSL_new(SSL_context)) || !SSL_set_fd(port->ssl, port->sock) || SSL_accept(port->ssl) <= 0) { fprintf(stderr,"Failed to initialize SSL connection: %s, errno: %d (%s)\n", ERR_reason_error_string(ERR_get_error()), errno, strerror(errno)); return STATUS_ERROR; } #endif /* ready for the normal startup packet */ PacketReceiveSetup(&port->pktInfo, readStartupPacket, (void *)port); return STATUS_OK; /* Do not close connection */ } /* Could add additional special packet types here */ #ifdef USE_SSL /* * Any SSL negotiation must have taken place here, so drop the connection * ASAP if we require SSL */ if (SecureNetServer && !port->ssl) { PacketSendError(&port->pktInfo, "Backend requires secure connection."); return STATUS_OK; } #endif /* Check we can handle the protocol the frontend is using. */ if (PG_PROTOCOL_MAJOR(port->proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) || PG_PROTOCOL_MAJOR(port->proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) || (PG_PROTOCOL_MAJOR(port->proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) && PG_PROTOCOL_MINOR(port->proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))) { PacketSendError(&port->pktInfo, "Unsupported frontend protocol."); return STATUS_OK; /* don't close the connection yet */ } /* * Get the parameters from the startup packet as C strings. The * packet destination was cleared first so a short packet has zeros * silently added and a long packet is silently truncated. */ StrNCpy(port->database, si->database, sizeof(port->database) - 1); StrNCpy(port->user, si->user, sizeof(port->user) - 1); StrNCpy(port->options, si->options, sizeof(port->options) - 1); StrNCpy(port->tty, si->tty, sizeof(port->tty) - 1); /* The database defaults to the user name. */ if (port->database[0] == '\0') StrNCpy(port->database, si->user, sizeof(port->database) - 1); /* Check a user name was given. */ if (port->user[0] == '\0') { PacketSendError(&port->pktInfo, "No Postgres username specified in startup packet."); return STATUS_OK; /* don't close the connection yet */ } /* Start the authentication itself. */ be_recvauth(port); return STATUS_OK; /* don't close the connection yet */ } /* * The client has sent a cancel request packet, not a normal * start-a-new-backend packet. Perform the necessary processing. * Note that in any case, we return STATUS_ERROR to close the * connection immediately. Nothing is sent back to the client. */ static int processCancelRequest(Port *port, PacketLen len, void *pkt) { CancelRequestPacket *canc = (CancelRequestPacket *) pkt; int backendPID; long cancelAuthCode; Dlelem *curr; Backend *bp; backendPID = (int) ntohl(canc->backendPID); cancelAuthCode = (long) ntohl(canc->cancelAuthCode); /* See if we have a matching backend */ for (curr = DLGetHead(BackendList); curr; curr = DLGetSucc(curr)) { bp = (Backend *) DLE_VAL(curr); if (bp->pid == backendPID) { if (bp->cancel_key == cancelAuthCode) { /* Found a match; signal that backend to cancel current op */ if (DebugLvl) fprintf(stderr, "%s: processCancelRequest: sending SIGINT to process %d\n", progname, bp->pid); kill(bp->pid, SIGINT); } else { /* Right PID, wrong key: no way, Jose */ if (DebugLvl) fprintf(stderr, "%s: processCancelRequest: bad key in cancel request for process %d\n", progname, bp->pid); } return STATUS_ERROR; } } /* No matching backend */ if (DebugLvl) fprintf(stderr, "%s: processCancelRequest: bad PID in cancel request for process %d\n", progname, backendPID); return STATUS_ERROR; } /* * ConnCreate -- create a local connection data structure */ static Port * ConnCreate(int serverFd) { Port *port; if (!(port = (Port *) calloc(1, sizeof(Port)))) { fprintf(stderr, "%s: ConnCreate: malloc failed\n", progname); SignalChildren(SIGUSR1); ExitPostmaster(1); } if (StreamConnection(serverFd, port) != STATUS_OK) { StreamClose(port->sock); free(port); port = NULL; } else { DLAddHead(PortList, DLNewElem(port)); RandomSalt(port->salt); port->pktInfo.state = Idle; } return port; } /* * ConnFree -- cree a local connection data structure */ void ConnFree(Port *conn) { #ifdef USE_SSL if (conn->ssl) { SSL_free(conn->ssl); } #endif free(conn); } /* * reset_shared -- reset shared memory and semaphores */ static void reset_shared(unsigned short port) { ipc_key = port * 1000 + shmem_seq * 100; CreateSharedMemoryAndSemaphores(ipc_key, MaxBackends); shmem_seq += 1; if (shmem_seq >= 10) shmem_seq -= 10; } /* * pmdie -- signal handler for cleaning up after a kill signal. */ static void pmdie(SIGNAL_ARGS) { PG_SETMASK(&BlockSig); TPRINTF(TRACE_VERBOSE, "pmdie %d", postgres_signal_arg); switch (postgres_signal_arg) { case SIGHUP: /* * Send SIGHUP to all children (update options flags) */ if (Shutdown > SmartShutdown) return; SignalChildren(SIGHUP); return; case SIGUSR2: /* * Send SIGUSR2 to all children (AsyncNotifyHandler) */ if (Shutdown > SmartShutdown) return; SignalChildren(SIGUSR2); return; case SIGTERM: /* * Smart Shutdown: * * let children to end their work and ShutdownDataBase. */ if (Shutdown >= SmartShutdown) return; Shutdown = SmartShutdown; tnow = time(NULL); fprintf(stderr, "Smart Shutdown request at %s", ctime(&tnow)); fflush(stderr); if (DLGetHead(BackendList)) /* let reaper() handle this */ return; /* * No children left. Shutdown data base system. */ if (StartupPID > 0 || FatalError) /* let reaper() handle this */ return; if (ShutdownPID > 0) abort(); ShutdownPID = ShutdownDataBase(); return; case SIGINT: /* * Fast Shutdown: * * abort all children with SIGTERM (rollback active * transactions and exit) and ShutdownDataBase. */ if (Shutdown >= FastShutdown) return; tnow = time(NULL); fprintf(stderr, "Fast Shutdown request at %s", ctime(&tnow)); fflush(stderr); if (DLGetHead(BackendList)) /* let reaper() handle this */ { Shutdown = FastShutdown; if (!FatalError) { fprintf(stderr, "Aborting any active transaction...\n"); fflush(stderr); SignalChildren(SIGTERM); } return; } if (Shutdown > NoShutdown) { Shutdown = FastShutdown; return; } Shutdown = FastShutdown; /* * No children left. Shutdown data base system. */ if (StartupPID > 0 || FatalError) /* let reaper() handle this */ return; if (ShutdownPID > 0) abort(); ShutdownPID = ShutdownDataBase(); return; case SIGQUIT: /* * Immediate Shutdown: * * abort all children with SIGUSR1 and exit without * attempt to properly shutdown data base system. */ tnow = time(NULL); fprintf(stderr, "Immediate Shutdown request at %s", ctime(&tnow)); fflush(stderr); if (ShutdownPID > 0) kill(ShutdownPID, SIGQUIT); else if (StartupPID > 0) kill(StartupPID, SIGQUIT); else if (DLGetHead(BackendList)) SignalChildren(SIGUSR1); break; } /* exit postmaster */ proc_exit(0); } /* * Reaper -- signal handler to cleanup after a backend (child) dies. */ static void reaper(SIGNAL_ARGS) { /* GH: replace waitpid for !HAVE_WAITPID. Does this work ? */ #ifdef HAVE_WAITPID int status; /* backend exit status */ #else union wait status; /* backend exit status */ #endif int exitstatus; int pid; /* process id of dead backend */ PG_SETMASK(&BlockSig); if (DebugLvl) fprintf(stderr, "%s: reaping dead processes...\n", progname); #ifdef HAVE_WAITPID while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { exitstatus = status; #else while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { exitstatus = status.w_status; #endif if (ShutdownPID > 0) { if (pid != ShutdownPID) abort(); if (exitstatus != 0) { fprintf(stderr, "Shutdown failed - abort\n"); fflush(stderr); proc_exit(1); } proc_exit(0); } if (StartupPID > 0) { if (pid != StartupPID) abort(); if (exitstatus != 0) { fprintf(stderr, "Startup failed - abort\n"); fflush(stderr); proc_exit(1); } StartupPID = 0; FatalError = false; if (Shutdown > NoShutdown) { if (ShutdownPID > 0) abort(); ShutdownPID = ShutdownDataBase(); } pqsignal(SIGCHLD, reaper); return; } CleanupProc(pid, exitstatus); } pqsignal(SIGCHLD, reaper); if (FatalError) { /* * Wait for all children exit then StartupDataBase. */ if (DLGetHead(BackendList)) return; if (StartupPID > 0 || ShutdownPID > 0) return; tnow = time(NULL); fprintf(stderr, "Server processes were terminated at %s" "Reinitializing shared memory and semaphores\n", ctime(&tnow)); fflush(stderr); shmem_exit(0); reset_shared(PostPortName); StartupPID = StartupDataBase(); return; } if (Shutdown > NoShutdown) { if (DLGetHead(BackendList)) return; if (StartupPID > 0 || ShutdownPID > 0) return; ShutdownPID = ShutdownDataBase(); } } /* * 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 *curr, *next; 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; } if (!FatalError) { tnow = time(NULL); fprintf(stderr, "Server process (pid %d) exited with status %d at %s" "Terminating any active server processes...\n", pid, exitstatus, ctime(&tnow)); fflush(stderr); } FatalError = true; curr = DLGetHead(BackendList); while (curr) { next = DLGetSucc(curr); bp = (Backend *) DLE_VAL(curr); /* * SIGUSR1 is the special signal that says exit * without proc_exit 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); } else { /* * I don't like that we call ProcRemove() here, assuming that * shmem may be corrupted! But is there another way to free * backend semaphores? Actually, I believe that we need not * in per backend semaphore at all (we use them to wait on lock * only, couldn't we just sigpause?), so probably we'll * remove this call from here someday. -- vadim 04-10-1999 */ ProcRemove(pid); DLRemove(curr); free(bp); DLFreeElem(curr); } curr = next; } } /* * Send a signal to all chidren processes. */ static void SignalChildren(int signal) { Dlelem *curr, *next; Backend *bp; int mypid = getpid(); curr = DLGetHead(BackendList); while (curr) { next = DLGetSucc(curr); bp = (Backend *) DLE_VAL(curr); if (bp->pid != mypid) { TPRINTF(TRACE_VERBOSE, "SignalChildren: sending signal %d to process %d", signal, bp->pid); kill(bp->pid, signal); } curr = next; } } /* * BackendStartup -- start backend process * * returns: STATUS_ERROR if the fork/exec failed, STATUS_OK * otherwise. * */ static int BackendStartup(Port *port) { Backend *bn; /* for backend cleanup */ int pid, i; #ifdef CYR_RECODE #define NR_ENVIRONMENT_VBL 6 char ChTable[80]; #else #define NR_ENVIRONMENT_VBL 5 #endif static char envEntry[NR_ENVIRONMENT_VBL][2 * ARGV_SIZE]; for (i = 0; i < NR_ENVIRONMENT_VBL; ++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", NextBackendTag); putenv(envEntry[1]); sprintf(envEntry[2], "PG_USER=%s", port->user); putenv(envEntry[2]); if (!getenv("PGDATA")) { sprintf(envEntry[3], "PGDATA=%s", DataDir); putenv(envEntry[3]); } sprintf(envEntry[4], "IPC_KEY=%d", ipc_key); putenv(envEntry[4]); #ifdef CYR_RECODE GetCharSetByHost(ChTable, port->raddr.in.sin_addr.s_addr, DataDir); if (*ChTable != '\0') { sprintf(envEntry[5], "PG_RECODETABLE=%s", ChTable); putenv(envEntry[5]); } #endif /* * Compute the cancel key that will be assigned to this backend. The * backend will have its own copy in the forked-off process' value of * MyCancelKey, so that it can transmit the key to the frontend. */ MyCancelKey = PostmasterRandom(); 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"); } /* * Flush stdio channels just before fork, to avoid double-output * problems. Ideally we'd use fflush(NULL) here, but there are still a * few non-ANSI stdio libraries out there (like SunOS 4.1.x) that * coredump if we do. Presently stdout and stderr are the only stdio * output channels used by the postmaster, so fflush'ing them should * be sufficient. */ fflush(stdout); fflush(stderr); if ((pid = fork()) == 0) { /* child */ if (DoBackend(port)) { fprintf(stderr, "%s child[%d]: BackendStartup: backend startup failed\n", progname, (int) getpid()); exit(1); } else exit(0); } /* in parent */ if (pid < 0) { fprintf(stderr, "%s: BackendStartup: fork failed: %s\n", progname, strerror(errno)); return STATUS_ERROR; } if (DebugLvl) fprintf(stderr, "%s: BackendStartup: pid %d user %s db %s socket %d\n", progname, pid, port->user, port->database, port->sock); /* Generate a new backend tag for every backend we start */ /* * XXX theoretically this could wrap around, if you have the patience * to start 2^31 backends ... */ NextBackendTag -= 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; bn->cancel_key = MyCancelKey; DLAddHead(BackendList, DLNewElem(bn)); return STATUS_OK; } /* * split_opts -- split a string of options and append it to an argv array * * NB: the string is destructively modified! * * 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... */ static void split_opts(char **argv, int *argcp, char *s) { while (s && *s) { while (isspace(*s)) ++s; if (*s == '\0') break; argv[(*argcp)++] = s; while (*s && !isspace(*s)) ++s; if (*s) *s++ = '\0'; } } /* * DoBackend -- set up the backend's argument list and invoke backend main(). * * This used to perform an execv() but we no longer exec the backend; * it's the same executable as the postmaster. * * returns: * Shouldn't return at all. * If PostgresMain() fails, return status. */ static int DoBackend(Port *port) { char *av[ARGV_SIZE * 2]; int ac = 0; char execbuf[MAXPGPATH]; char debugbuf[ARGV_SIZE]; char protobuf[ARGV_SIZE]; char dbbuf[ARGV_SIZE]; char optbuf[ARGV_SIZE]; char ttybuf[ARGV_SIZE]; int i; struct timeval now; struct timezone tz; /* * Let's clean up ourselves as the postmaster child */ /* We don't want the postmaster's proc_exit() handlers */ on_exit_reset(); /* * Signal handlers setting is moved to tcop/postgres... */ /* Close the postmaster sockets */ if (NetServer) StreamClose(ServerSock_INET); #ifndef __CYGWIN32__ StreamClose(ServerSock_UNIX); #endif /* Save port etc. for ps status */ MyProcPort = port; MyProcPid = getpid(); /* * Don't want backend to be able to see the postmaster random number * generator state. We have to clobber the static random_seed *and* * start a new random sequence in the random() library function. */ random_seed = 0; gettimeofday(&now, &tz); srandom(now.tv_usec); /* ---------------- * Now, build the argv vector that will be given to PostgresMain. * * The layout of the command line is * postgres [secure switches] -p databasename [insecure switches] * where the switches after -p come from the client request. * ---------------- */ StrNCpy(execbuf, Execfile, MAXPGPATH); av[ac++] = execbuf; /* * We need to set our argv[0] to an absolute path name because some * OS's use this for dynamic loading, like BSDI. Without it, when we * change directories to the database dir, the dynamic loader can't * find the base executable and fails. Another advantage is that this * changes the 'ps' displayed process name on some platforms. It does * on BSDI. That's a big win. */ #ifndef linux /* * This doesn't work on linux and overwrites the only valid pointer to * the argv buffer. See PS_INIT_STATUS macro. */ real_argv[0] = Execfile; #endif /* * Pass the requested debugging level along to the backend. * 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; } /* * Pass any backend switches specified with -o in the postmaster's own * command line. We assume these are secure. (It's OK to mangle * ExtraOptions since we are now in the child process; this won't * change the postmaster's copy.) */ split_opts(av, &ac, ExtraOptions); /* Tell the backend what protocol the frontend is using. */ sprintf(protobuf, "-v%u", port->proto); av[ac++] = protobuf; /* * Tell the backend it is being called from the postmaster, and which * database to use. -p marks the end of secure switches. */ av[ac++] = "-p"; StrNCpy(dbbuf, port->database, ARGV_SIZE); av[ac++] = dbbuf; /* * Pass the (insecure) option switches from the connection request. */ StrNCpy(optbuf, port->options, ARGV_SIZE); split_opts(av, &ac, optbuf); /* * Pass the (insecure) debug output file request. * * NOTE: currently, this is useless code, since the backend will not * honor an insecure -o switch. I left it here since the backend * could be modified to allow insecure -o, given adequate checking * that the specified filename is something safe to write on. */ if (port->tty[0]) { StrNCpy(ttybuf, port->tty, ARGV_SIZE); av[ac++] = "-o"; av[ac++] = ttybuf; } av[ac] = (char *) NULL; if (DebugLvl > 1) { fprintf(stderr, "%s child[%d]: starting with (", progname, MyProcPid); for (i = 0; i < ac; ++i) fprintf(stderr, "%s ", av[i]); fprintf(stderr, ")\n"); } return (PostgresMain(ac, av, real_argc, real_argv)); } /* * 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. * * MUST -- vadim 05-10-1999 */ if (ServerSock_INET != INVALID_SOCK) StreamClose(ServerSock_INET); #ifndef __CYGWIN32__ if (ServerSock_UNIX != INVALID_SOCK) StreamClose(ServerSock_UNIX); #endif proc_exit(status); } static void dumpstatus(SIGNAL_ARGS) { Dlelem *curr; PG_SETMASK(&BlockSig); curr = DLGetHead(PortList); while (curr) { Port *port = DLE_VAL(curr); fprintf(stderr, "%s: dumpstatus:\n", progname); fprintf(stderr, "\tsock %d\n", port->sock); curr = DLGetSucc(curr); } } /* * CharRemap */ static char CharRemap(long int ch) { if (ch < 0) ch = -ch; ch = ch % 62; if (ch < 26) return 'A' + ch; ch -= 26; if (ch < 26) return 'a' + ch; ch -= 26; return '0' + ch; } /* * RandomSalt */ static void RandomSalt(char *salt) { long rand = PostmasterRandom(); *salt = CharRemap(rand % 62); *(salt + 1) = CharRemap(rand / 62); } /* * PostmasterRandom */ static long PostmasterRandom(void) { static bool initialized = false; if (!initialized) { Assert(random_seed != 0 && !IsUnderPostmaster); srandom(random_seed); initialized = true; } return random() ^ random_seed; } /* * Count up number of child processes. */ static int CountChildren(void) { Dlelem *curr; Backend *bp; int mypid = getpid(); int cnt = 0; for (curr = DLGetHead(BackendList); curr; curr = DLGetSucc(curr)) { bp = (Backend *) DLE_VAL(curr); if (bp->pid != mypid) cnt++; } return cnt; } #ifdef USE_SSL /* * Initialize SSL library and structures */ static void InitSSL(void) { char fnbuf[2048]; SSL_load_error_strings(); SSL_library_init(); SSL_context = SSL_CTX_new(SSLv23_method()); if (!SSL_context) { fprintf(stderr, "Failed to create SSL context: %s\n",ERR_reason_error_string(ERR_get_error())); exit(1); } snprintf(fnbuf,sizeof(fnbuf),"%s/server.crt", DataDir); if (!SSL_CTX_use_certificate_file(SSL_context, fnbuf, SSL_FILETYPE_PEM)) { fprintf(stderr, "Failed to load server certificate (%s): %s\n",fnbuf,ERR_reason_error_string(ERR_get_error())); exit(1); } snprintf(fnbuf,sizeof(fnbuf),"%s/server.key", DataDir); if (!SSL_CTX_use_PrivateKey_file(SSL_context, fnbuf, SSL_FILETYPE_PEM)) { fprintf(stderr, "Failed to load private key file (%s): %s\n",fnbuf,ERR_reason_error_string(ERR_get_error())); exit(1); } if (!SSL_CTX_check_private_key(SSL_context)) { fprintf(stderr, "Check of private key failed: %s\n",ERR_reason_error_string(ERR_get_error())); exit(1); } } #endif static pid_t SSDataBase(bool startup) { pid_t pid; int i; static char ssEntry[4][2 * ARGV_SIZE]; for (i = 0; i < 4; ++i) MemSet(ssEntry[i], 0, 2 * ARGV_SIZE); sprintf(ssEntry[0], "POSTPORT=%d", PostPortName); putenv(ssEntry[0]); sprintf(ssEntry[1], "POSTID=%d", NextBackendTag); putenv(ssEntry[1]); if (!getenv("PGDATA")) { sprintf(ssEntry[2], "PGDATA=%s", DataDir); putenv(ssEntry[2]); } sprintf(ssEntry[3], "IPC_KEY=%d", ipc_key); putenv(ssEntry[3]); fflush(stdout); fflush(stderr); if ((pid = fork()) == 0) /* child */ { char *av[ARGV_SIZE * 2]; int ac = 0; char execbuf[MAXPGPATH]; char nbbuf[ARGV_SIZE]; char dbbuf[ARGV_SIZE]; on_exit_reset(); if (NetServer) StreamClose(ServerSock_INET); #ifndef __CYGWIN32__ StreamClose(ServerSock_UNIX); #endif StrNCpy(execbuf, Execfile, MAXPGPATH); av[ac++] = execbuf; av[ac++] = "-d"; sprintf(nbbuf, "-B%u", NBuffers); av[ac++] = nbbuf; if (startup) av[ac++] = "-x"; av[ac++] = "-p"; StrNCpy(dbbuf, "template1", ARGV_SIZE); av[ac++] = dbbuf; av[ac] = (char *) NULL; optind = 1; pqsignal(SIGQUIT, SIG_DFL); #ifdef HAVE_SIGPROCMASK sigdelset(&BlockSig, SIGQUIT); #else BlockSig &= ~(sigmask(SIGQUIT)); #endif PG_SETMASK(&BlockSig); BootstrapMain(ac, av); exit(0); } /* in parent */ if (pid < 0) { fprintf(stderr, "%s Data Base: fork failed: %s\n", ((startup) ? "Startup" : "Shutdown"), strerror(errno)); ExitPostmaster(1); } NextBackendTag -= 1; return(pid); } static char PidFile[MAXPGPATH]; static void UnlinkPidFile(void) { unlink(PidFile); } static int SetPidFile(pid_t pid, char *progname, int port, char *datadir, int assert, int nbuf, char *execfile, int debuglvl, int netserver, #ifdef USE_SSL int securenetserver, #endif int maxbackends, int reinit, int silent, int sendstop, char *extraoptions) { int fd; char optsfile[MAXPGPATH]; char pidstr[32]; int len; pid_t post_pid; char opts[1024]; char buf[1024]; /* * Creating pid file */ snprintf(PidFile, sizeof(PidFile), "%s/%s", datadir, PIDFNAME); fd = open(PidFile, O_RDWR | O_CREAT | O_EXCL, 0600); if (fd < 0) { /* * Couldn't create the pid file. Probably * it already exists. Read the file to see if the process * actually exists */ fd = open(PidFile, O_RDONLY, 0600); if (fd < 0) { fprintf(stderr, "Can't create/read pid file: %s\n", PidFile); fprintf(stderr, "Please check the permission and try again.\n"); return(-1); } if ((len = read(fd, pidstr, sizeof(pidstr)-1)) < 0) { fprintf(stderr, "Can't create/read pid file: %s\n", PidFile); fprintf(stderr, "Please check the permission and try again.\n"); close(fd); return(-1); } close(fd); /* * Check to see if the process actually exists */ pidstr[len] = '\0'; post_pid = (pid_t)atoi(pidstr); if (post_pid == 0 || (post_pid > 0 && kill(post_pid, 0) < 0)) { /* * No, the process did not exist. Unlink * the file and try to create it */ if (unlink(PidFile) < 0) { fprintf(stderr, "Can't remove pidfile: %s\n", PidFile); fprintf(stderr, "The file seems accidently left, but I couldn't remove it.\n"); fprintf(stderr, "Please remove the file by hand and try again.\n"); return(-1); } fd = open(PidFile, O_RDWR | O_CREAT | O_EXCL, 0600); if (fd < 0) { fprintf(stderr, "Can't create pidfile: %s\n", PidFile); fprintf(stderr, "Please check the permission and try again.\n"); return(-1); } } else { /* * Another postmaster is running */ fprintf(stderr, "Can't create pidfile: %s\n", PidFile); fprintf(stderr, "Is another postmaser (pid: %s) running?\n", pidstr); return(-1); } } sprintf(pidstr, "%d", pid); if (write(fd, pidstr, strlen(pidstr)) != strlen(pidstr)) { fprintf(stderr,"Write to pid file failed\n"); fprintf(stderr, "Please check the permission and try again.\n"); close(fd); unlink(PidFile); return(-1); } close(fd); /* * Creating opts file */ snprintf(optsfile, sizeof(optsfile), "%s/%s", datadir, OPTSFNAME); fd = open(optsfile, O_RDWR | O_TRUNC | O_CREAT, 0600); if (fd < 0) { fprintf(stderr, "Can't create optsfile:%s", optsfile); unlink(PidFile); return(-1); } snprintf(opts, sizeof(opts), "%s\n-p %d\n-D %s\n",progname, port, datadir); if (assert) { sprintf(buf, "-A %d\n", assert); strcat(opts, buf); } snprintf(buf, sizeof(buf), "-B %d\n-b %s\n",nbuf, execfile); strcat(opts, buf); if (debuglvl) { sprintf(buf, "-d %d\n", debuglvl); strcat(opts, buf); } if (netserver) { strcat(opts, "-i\n"); } #ifdef USE_SSL if (securenetserver) { strcat(opts, "-l\n"); } #endif snprintf(buf, sizeof(buf), "-N %d\n", maxbackends); strcat(opts, buf); if (!reinit) { strcat(opts, "-n\n"); } if (silent) { strcat(opts, "-S\n"); } if (sendstop) { strcat(opts, "-s\n"); } if (strlen(extraoptions) > 0) { strcat(opts, "-o '"); strcat(opts, extraoptions); strcat(opts, "'"); } if (write(fd, opts, strlen(opts)) != strlen(opts)) { perror("Writing to opts file failed"); unlink(PidFile); close(fd); return(-1); } close(fd); /* * register clean up proc */ on_proc_exit(UnlinkPidFile, NULL); return(0); }