📄 postmaster.c
字号:
* This check is an essential part of the interlock that prevents two * postmasters from starting in the same directory (see CreateLockFile()). * Do not remove or weaken it. * * XXX can we safely enable this check on Windows? */#if !defined(WIN32) && !defined(__CYGWIN__) if (stat_buf.st_uid != geteuid()) ereport(FATAL, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("data directory \"%s\" has wrong ownership", DataDir), errhint("The server must be started by the user that owns the data directory.")));#endif /* * Check if the directory has group or world access. If so, reject. * * It would be possible to allow weaker constraints (for example, allow * group access) but we cannot make a general assumption that that is * okay; for example there are platforms where nearly all users * customarily belong to the same group. Perhaps this test should be * configurable. * * XXX temporarily suppress check when on Windows, because there may not * be proper support for Unix-y file permissions. Need to think of a * reasonable check to apply on Windows. */#if !defined(WIN32) && !defined(__CYGWIN__) if (stat_buf.st_mode & (S_IRWXG | S_IRWXO)) ereport(FATAL, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("data directory \"%s\" has group or world access", DataDir), errdetail("Permissions should be u=rwx (0700).")));#endif /* Look for PG_VERSION before looking for pg_control */ ValidatePgVersion(DataDir); snprintf(path, sizeof(path), "%s/global/pg_control", DataDir); fp = AllocateFile(path, PG_BINARY_R); if (fp == NULL) { write_stderr("%s: could not find the database system\n" "Expected to find it in the directory \"%s\",\n" "but could not open file \"%s\": %s\n", progname, DataDir, path, strerror(errno)); ExitPostmaster(2); } FreeFile(fp);}#ifdef USE_BONJOUR/* * empty callback function for DNSServiceRegistrationCreate() */static voidreg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context){}#endif /* USE_BONJOUR *//* * Fork away from the controlling terminal (-S option) */static voidpmdaemonize(void){#ifndef WIN32 int i; pid_t pid; pid = fork_process(); if (pid == (pid_t) -1) { write_stderr("%s: could not fork background process: %s\n", progname, strerror(errno)); ExitPostmaster(1); } else if (pid) { /* parent */ /* Parent should just exit, without doing any atexit cleanup */ _exit(0); } MyProcPid = PostmasterPid = getpid(); /* reset PID vars to child *//* 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) { write_stderr("%s: could not dissociate from controlling TTY: %s\n", progname, strerror(errno)); ExitPostmaster(1); }#endif i = open(NULL_DEV, O_RDWR); dup2(i, 0); dup2(i, 1); dup2(i, 2); close(i);#else /* WIN32 */ /* not supported */ elog(FATAL, "SilentMode not supported under WIN32");#endif /* WIN32 */}/* * Print out help message */static voidusage(const char *progname){ printf(_("%s is the PostgreSQL server.\n\n"), progname); printf(_("Usage:\n %s [OPTION]...\n\n"), progname); printf(_("Options:\n"));#ifdef USE_ASSERT_CHECKING printf(_(" -A 1|0 enable/disable run-time assert checking\n"));#endif printf(_(" -B NBUFFERS number of shared buffers\n")); printf(_(" -c NAME=VALUE set run-time parameter\n")); printf(_(" -d 1-5 debugging level\n")); printf(_(" -D DATADIR database directory\n")); printf(_(" -F turn fsync off\n")); printf(_(" -h HOSTNAME host name or IP address to listen on\n")); printf(_(" -i enable TCP/IP connections\n")); printf(_(" -k DIRECTORY Unix-domain socket location\n"));#ifdef USE_SSL printf(_(" -l enable SSL connections\n"));#endif printf(_(" -N MAX-CONNECT maximum number of allowed connections\n")); printf(_(" -o OPTIONS pass \"OPTIONS\" to each server process\n")); printf(_(" -p PORT port number to listen on\n")); printf(_(" -S silent mode (start in background without logging output)\n")); printf(_(" --help show this help, then exit\n")); printf(_(" --version output version information, then exit\n")); printf(_("\nDeveloper options:\n")); printf(_(" -n do not reinitialize shared memory after abnormal exit\n")); printf(_(" -s send SIGSTOP to all backend servers if one dies\n")); printf(_("\nPlease read the documentation for the complete list of run-time\n" "configuration settings and how to set them on the command line or in\n" "the configuration file.\n\n" "Report bugs to <pgsql-bugs@postgresql.org>.\n"));}/* * Main idle loop of postmaster */static intServerLoop(void){ fd_set readmask; int nSockets; time_t now, last_touch_time; struct timeval earlier, later; gettimeofday(&earlier, NULL); last_touch_time = time(NULL); nSockets = initMasks(&readmask); for (;;) { Port *port; fd_set rmask; struct timeval timeout; int selres; int i; /* * Wait for something to happen. * * We wait at most one minute, or the minimum autovacuum delay, to * ensure that the other background tasks handled below get done even * when no requests are arriving. */ memcpy((char *) &rmask, (char *) &readmask, sizeof(fd_set)); timeout.tv_sec = Min(60, autovacuum_naptime); timeout.tv_usec = 0; PG_SETMASK(&UnBlockSig); selres = select(nSockets, &rmask, NULL, NULL, &timeout); /* * Block all signals until we wait again. (This makes it safe for our * signal handlers to do nontrivial work.) */ PG_SETMASK(&BlockSig); if (selres < 0) { if (errno != EINTR && errno != EWOULDBLOCK) { ereport(LOG, (errcode_for_socket_access(), errmsg("select() failed in postmaster: %m"))); return STATUS_ERROR; } } /* * New connection pending on any of our sockets? If so, fork a child * process to deal with it. */ if (selres > 0) { /* * Select a random seed at the time of first receiving a request. */ while (random_seed == 0) { gettimeofday(&later, NULL); /* * We are not sure how much precision is in tv_usec, so we * swap the high and low 16 bits of 'later' and XOR them with * 'earlier'. On the off chance that the result is 0, we loop * until it isn't. */ random_seed = earlier.tv_usec ^ ((later.tv_usec << 16) | ((later.tv_usec >> 16) & 0xffff)); } for (i = 0; i < MAXLISTEN; i++) { if (ListenSocket[i] == -1) break; if (FD_ISSET(ListenSocket[i], &rmask)) { port = ConnCreate(ListenSocket[i]); if (port) { BackendStartup(port); /* * We no longer need the open socket or port structure * in this process */ StreamClose(port->sock); ConnFree(port); } } } } /* If we have lost the system logger, try to start a new one */ if (SysLoggerPID == 0 && Redirect_stderr) SysLoggerPID = SysLogger_Start(); /* * If no background writer process is running, and we are not in a * state that prevents it, start one. It doesn't matter if this * fails, we'll just try again later. */ if (BgWriterPID == 0 && StartupPID == 0 && !FatalError) { BgWriterPID = StartBackgroundWriter(); /* If shutdown is pending, set it going */ if (Shutdown > NoShutdown && BgWriterPID != 0) kill(BgWriterPID, SIGUSR2); } /* * Start a new autovacuum process, if there isn't one running already. * (It'll die relatively quickly.) We check that it's not started too * frequently in autovac_start. */ if (AutoVacuumingActive() && AutoVacPID == 0 && StartupPID == 0 && !FatalError && Shutdown == NoShutdown) AutoVacPID = autovac_start(); /* If we have lost the archiver, try to start a new one */ if (XLogArchivingActive() && PgArchPID == 0 && StartupPID == 0 && !FatalError && Shutdown == NoShutdown) PgArchPID = pgarch_start(); /* If we have lost the stats collector, try to start a new one */ if (PgStatPID == 0 && StartupPID == 0 && !FatalError && Shutdown == NoShutdown) PgStatPID = pgstat_start(); /* * Touch the socket and lock file every 58 minutes, to ensure that * they are not removed by overzealous /tmp-cleaning tasks. We assume * no one runs cleaners with cutoff times of less than an hour ... */ now = time(NULL); if (now - last_touch_time >= 58 * SECS_PER_MINUTE) { TouchSocketFile(); TouchSocketLockFile(); last_touch_time = now; } }}/* * Initialise the masks for select() for the ports we are listening on. * Return the number of sockets to listen on. */static intinitMasks(fd_set *rmask){ int nsocks = -1; int i; FD_ZERO(rmask); for (i = 0; i < MAXLISTEN; i++) { int fd = ListenSocket[i]; if (fd == -1) break; FD_SET(fd, rmask); if (fd > nsocks) nsocks = fd; } return nsocks + 1;}/* * Read the startup packet and do something according to it. * * Returns STATUS_OK or STATUS_ERROR, or might call ereport(FATAL) and * not return at all. * * (Note that ereport(FATAL) stuff is sent to the client, so only use it * if that's what you want. Return STATUS_ERROR if you don't want to * send anything to the client, which would typically be appropriate * if we detect a communications failure.) */static intProcessStartupPacket(Port *port, bool SSLdone){ int32 len; void *buf; ProtocolVersion proto; MemoryContext oldcontext; if (pq_getbytes((char *) &len, 4) == EOF) { /* * EOF after SSLdone probably means the client didn't like our * response to NEGOTIATE_SSL_CODE. That's not an error condition, so * don't clutter the log with a complaint. */ if (!SSLdone) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("incomplete startup packet"))); return STATUS_ERROR; } len = ntohl(len); len -= 4; if (len < (int32) sizeof(ProtocolVersion) || len > MAX_STARTUP_PACKET_LENGTH) { ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid length of startup packet"))); return STATUS_ERROR; } /* * Allocate at least the size of an old-style startup packet, plus one * extra byte, and make sure all are zeroes. This ensures we will have * null termination of all strings, in both fixed- and variable-length * packet layouts. */ if (len <= (int32) sizeof(StartupPacket)) buf = palloc0(sizeof(StartupPacket) + 1); else buf = palloc0(len + 1); if (pq_getbytes(buf, len) == EOF) { ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("incomplete startup packet"))); return STATUS_ERROR; } /* * The first field is either a protocol version number or a special * request code. */ port->proto = proto = ntohl(*((ProtocolVersion *) buf)); if (proto == CANCEL_REQUEST_CODE) { processCancelRequest(port, buf); return 127; /* XXX */ } if (proto == NEGOTIATE_SSL_CODE && !SSLdone) { char SSLok;#ifdef USE_SSL /* No SSL when disabled or on Unix sockets */ if (!EnableSSL || IS_AF_UNIX(port->laddr.addr.ss_family)) SSLok = 'N'; else SSLok = 'S'; /* Support for SSL */#else SSLok = 'N'; /* No support for SSL */#endif if (send(port->sock, &SSLok, 1, 0) != 1) { ereport(COMMERROR, (errcode_for_socket_access(), errmsg("failed to send SSL negotiation response: %m"))); return STATUS_ERROR; /* close the connection */ }#ifdef USE_SSL if (SSLok == 'S' && secure_open_server(port) == -1) return STATUS_ERROR;#endif /* regular startup packet, cancel, etc packet should follow... */ /* but not another SSL negotiation request */ return ProcessStartupPacket(port, true); } /* Could add additional special packet types here */ /* * Set FrontendProtocol now so that ereport() knows what format to send if * we fail during startup. */ FrontendProtocol = proto; /* Check we can handle the protocol the frontend is using. */ if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) || PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) || (PG_PROTOCOL_MAJOR(proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) && PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))) ereport(FATAL, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u", PG_PROTOCOL_MAJOR(proto), PG_PROTOCOL_MINOR(proto), PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST), PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST), PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))); /* * Now fetch parameters out of startup packet and save them into the Port * structure. All data structures attached to the Port struct must be * allocated in TopMemoryContext so that they won't disappear when we pass * them to PostgresMain (see BackendRun). We need not worry about leaking * this storage on failure, since we aren't in the postmaster process * anymore. */ oldcontext = MemoryContextSwitchTo(TopMemoryContext); if (PG_PROTOCOL_MAJOR(proto) >= 3) { int32 offset = sizeof(ProtocolVersion); /* * Scan packet body for name/option pairs. We can assume any string * beginning within the packet body is null-terminated, thanks to * zeroing extra byte above. */ port->guc_options = NIL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -