📄 postmaster.c
字号:
/*------------------------------------------------------------------------- * * 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: /usr/local/cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.108 1999/07/07 17:17:48 momjian 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: * *------------------------------------------------------------------------- */ /* moved here to prevent double define */#include <sys/param.h> /* for MAXHOSTNAMELEN on most */#ifdef HAVE_NETDB_H#include <netdb.h> /* for MAXHOSTNAMELEN on some */#endif#ifndef MAXHOSTNAMELEN#define MAXHOSTNAMELEN 256#endif#include "postgres.h"#include <signal.h>#include <string.h>#include <stdlib.h>#include <time.h>#if !defined(NO_UNISTD_H)#include <unistd.h>#endif /* !NO_UNISTD_H */#include <ctype.h>#include <sys/types.h> /* for fd_set stuff */#include <sys/stat.h> /* for umask */#include <sys/time.h>#include <sys/socket.h>#ifdef HAVE_LIMITS_H#include <limits.h>#else#include <values.h>#endif#include <sys/wait.h>#include <errno.h>#include <fcntl.h>#include <stdio.h>#ifdef HAVE_SYS_SELECT_H#include <sys/select.h>#endif#ifdef __CYGWIN32__#include <getopt.h>#endif#include "storage/ipc.h"#include "libpq/libpq.h"#include "libpq/auth.h"#include "libpq/pqcomm.h"#include "libpq/pqsignal.h"#include "libpq/crypt.h"#include "miscadmin.h"#include "version.h"#include "lib/dllist.h"#include "tcop/tcopprot.h"#include "commands/async.h"#include "nodes/nodes.h"#include "utils/mcxt.h"#include "storage/proc.h"#include "utils/elog.h"#ifndef HAVE_GETHOSTNAME#include "port-protos.h" /* For gethostname() */#endif#include "storage/fd.h"#include "utils/trace.h"#if !defined(MAXINT)#define MAXINT INT_MAX#endif#define INVALID_SOCK (-1)#define ARGV_SIZE 64 /* * Max time in seconds for socket to linger (close() to block) waiting * for frontend to retrieve its message from us. *//* * 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;static short ActiveBackends = FALSE; /* * 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;/* * Default Values */static char Execfile[MAXPATHLEN] = "";static int ServerSock_INET = INVALID_SOCK; /* stream socket server */#ifndef __CYGWIN32__static int ServerSock_UNIX = INVALID_SOCK; /* stream socket server */#endif/* * Set by the -o option */static char ExtraOptions[MAXPATHLEN] = "";/* * 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 *//* * GH: For !HAVE_SIGPROCMASK (NEXTSTEP), TRH implemented an * alternative interface. */#ifdef HAVE_SIGPROCMASKstatic sigset_t oldsigmask, newsigmask;#elsestatic int orgsigmask = sigblock(0);#endif/* * 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(void);static Port *ConnCreate(int serverFd);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);#ifdef CYR_RECODEvoid GetCharSetByHost(char *, int, char *);#endif#ifdef USE_ASSERT_CHECKINGint assert_enabled = 1;#endifstatic voidcheckDataDir(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[MAXPATHLEN]; FILE *fp; sprintf(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; } }}intPostmasterMain(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; /* * 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:dim: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)) 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 < nonblank_argc) && *argv[optind] != '-') { DebugLvl = atoi(argv[optind]); optind++; } else DebugLvl = 1; pg_options[TRACE_VERBOSE] = DebugLvl; break; case 'i': NetServer = true; break; 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); 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); } 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -