/*------------------------------------------------------------------------- * * 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. * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.163 2000/08/29 16:40:19 tgl 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 "postgres.h" #include #include #include #include #include #include #include #include #include #include #include #include /* moved here to prevent double define */ #include #include #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_GETOPT_H #include "getopt.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/exc.h" #include "utils/guc.h" #include "utils/memutils.h" #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; int PostPortName; /* * 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. */ 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 = INT_MAX; /* XXX why count down not up? */ static char *progname = (char *) NULL; static char **real_argv; static int real_argc; static time_t tnow; /* flag to indicate that SIGHUP arrived during server loop */ static volatile bool got_SIGHUP = false; /* * Default Values */ static int ServerSock_INET = INVALID_SOCK; /* stream socket server */ #ifdef HAVE_UNIX_SOCKETS 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; bool NetServer = false; /* listen on TCP/IP */ #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(int argc, char *argv[]); static Port *ConnCreate(int serverFd); static void ConnFree(Port *port); static void reset_shared(int port); static void SIGHUP_handler(SIGNAL_ARGS); 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 bool CreateOptsFile(int argc, char *argv[]); 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 static void checkDataDir(const char *DataDir) { char path[MAXPGPATH]; FILE *fp; 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); exit(2); } snprintf(path, sizeof(path), "%s%cbase%ctemplate1%cpg_class", DataDir, SEP_CHAR, SEP_CHAR, SEP_CHAR); fp = AllocateFile(path, PG_BINARY_R); if (fp == NULL) { fprintf(stderr, "%s does not find the database system." "\n\tExpected to find it in the PGDATA directory \"%s\"," "\n\tbut unable to open file \"%s\": %s\n\n", progname, DataDir, path, strerror(errno)); exit(2); } FreeFile(fp); ValidatePgVersion(DataDir); } int PostmasterMain(int argc, char *argv[]) { int opt; int status; int silentflag = 0; char original_extraoptions[MAXPGPATH]; IsUnderPostmaster = true; /* so that backends know this */ *original_extraoptions = '\0'; progname = argv[0]; real_argv = argv; real_argc = argc; /* * for security, no dir or file created can be group or other * accessible */ umask((mode_t) 0077); MyProcPid = getpid(); /* * Fire up essential subsystems: error and memory management */ EnableExceptionHandling(true); MemoryContextInit(); /* * By default, palloc() requests in the postmaster will be allocated * in the PostmasterContext, which is space that can be recycled by * backends. Allocated data that needs to be available to backends * should be allocated in TopMemoryContext. */ PostmasterContext = AllocSetContextCreate(TopMemoryContext, "Postmaster", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); MemoryContextSwitchTo(PostmasterContext); /* * Options setup */ if (getenv("PGDATA")) DataDir = strdup(getenv("PGDATA")); /* default value */ ResetAllOptions(); /* * First we must scan for a -D argument to get the data dir. Then * read the config file. Finally, scan all the other arguments. * (Command line switches override config file.) * * Note: The two lists of options must be exactly the same, even * though perhaps the first one would only have to be "D:" with * opterr turned off. But some versions of getopt (notably GNU) * are going to arbitrarily permute some "non-options" (according * to the local world view) which will result in some switches * being associated with the wrong argument. Death and destruction * will occur. */ opterr = 1; while ((opt = getopt(argc, argv, "A:a:B:b:D:d:Film:MN:no:p:Ss-:?")) != EOF) { switch(opt) { case 'D': if (DataDir) free(DataDir); DataDir = strdup(optarg); break; case '-': { char *name, *value; ParseLongOption(optarg, &name, &value); if (strcmp(name, "help")==0) { usage(progname); exit(0); } else if (strcmp(name, "version")==0) { puts("postmaster (PostgreSQL) " PG_VERSION); exit(0); } break; } case '?': if (strcmp(argv[optind - 1], "-?") == 0) { usage(progname); exit(0); } else { fprintf(stderr, "Try -? for help.\n"); exit(1); } break; } } optind = 1; /* start over */ checkDataDir(DataDir); /* issues error messages */ ProcessConfigFile(PGC_POSTMASTER); IgnoreSystemIndexes(false); while ((opt = getopt(argc, argv, "A:a:B:b:D:d:Film:MN:no:p:Ss-:?")) != EOF) { switch (opt) { case 'A': #ifndef USE_ASSERT_CHECKING fprintf(stderr, "Assert checking is not compiled in\n"); #else assert_enabled = atoi(optarg); #endif break; case 'a': /* Can no longer set authentication method. */ break; case 'B': NBuffers = atoi(optarg); break; case 'b': /* Can no longer set the backend executable file to use. */ break; case 'D': /* already done above */ break; case 'd': /* * Turn on debugging for the postmaster and the backend * servers descended from it. */ DebugLvl = atoi(optarg); break; case 'F': enableFsync = false; 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': PostPortName = 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; case '-': { char *name, *value; ParseLongOption(optarg, &name, &value); if (!value) elog(ERROR, "--%s requires argument", optarg); SetConfigOption(name, value, PGC_POSTMASTER); free(name); if (value) free(value); break; } default: /* shouldn't get here */ fprintf(stderr, "Try -? for help.\n"); exit(1); } } /* * Non-option switch arguments don't exist. */ if (optind < argc) { fprintf(stderr, "%s: invalid argument -- %s\n", progname, argv[optind]); exit(1); } /* * 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: The number of buffers (-B) must be at least twice the number of allowed connections (-N) and at least 16.\n", progname); exit(1); } #ifdef USE_SSL if (!NetServer && SecureNetServer) { fprintf(stderr, "%s: For SSL, you must enable TCP/IP connections.\n", progname); exit(1); } InitSSL(); #endif if (NetServer) { status = StreamServerPort(AF_INET, (unsigned short)PostPortName, &ServerSock_INET); if (status != STATUS_OK) { fprintf(stderr, "%s: cannot create INET stream port\n", progname); exit(1); } } #ifdef HAVE_UNIX_SOCKETS status = StreamServerPort(AF_UNIX, (unsigned short)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 */ 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(argc, argv); else { /* * create pid file. if the file has already existed, exits. */ SetPidFname(DataDir); if (SetPidFile(getpid()) == 0) { if (!CreateOptsFile(argc, argv)) { UnlinkPidFile(); ExitPostmaster(1); return 0; /* not reached */ } } else { ExitPostmaster(1); return 0; /* not reached */ } /* * register clean up proc */ on_proc_exit(UnlinkPidFile, NULL); } /* * Set up signal handlers for the postmaster process. */ pqinitmask(); PG_SETMASK(&BlockSig); pqsignal(SIGHUP, SIGHUP_handler); /* reread config file and have children do same */ 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(int argc, char *argv[]) { int i; pid_t pid; SetPidFname(DataDir); 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) == 0) { if (!CreateOptsFile(argc, argv)) { /* * Failed to create opts file. kill the child and exit * now. */ UnlinkPidFile(); kill(pid, SIGTERM); ExitPostmaster(1); return; /* not reached */ } _exit(0); } else { /* * Failed to create pid file. kill the child and exit now. */ kill(pid, SIGTERM); ExitPostmaster(1); return; /* not reached */ } } /* 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 i = open(NULL_DEV, O_RDWR | PG_BINARY); dup2(i, 0); dup2(i, 1); dup2(i, 2); close(i); /* * register clean up proc */ on_proc_exit(UnlinkPidFile, NULL); } /* * Print out help message */ static void usage(const char *progname) { printf("%s is the PostgreSQL server.\n\n", progname); printf("Usage:\n %s [options]\n\n", progname); printf("Options:\n"); #ifdef USE_ASSERT_CHECKING printf(" -A 1|0 enable/disable runtime assert checking\n"); #endif printf(" -B number of shared buffers\n"); printf(" -d 1-5 debugging level\n"); printf(" -D database directory\n"); printf(" -F turn fsync off\n"); printf(" -i listen on TCP/IP sockets\n"); #ifdef USE_SSL printf(" -l listen only on SSL connections (EXPERIMENTAL)\n"); #endif printf(" -N maximum number of allowed connections (1..%d, default %d)\n", MAXBACKENDS, DEF_MAXBACKENDS); printf(" -o