📄 postmaster.c
字号:
{ /* * Found entry for freshly-dead backend, so remove it. */ DLRemove(curr); free(bp); DLFreeElem(curr); } curr = next; } if (pid == CheckPointPID) { CheckPointPID = 0; checkpointed = 0; } else { /* * Tell the collector about backend termination */ pgstat_beterm(pid); } FatalError = true;}/* * Log the death of a child process. */static voidLogChildExit(int lev, const char *procname, int pid, int exitstatus){ if (WIFEXITED(exitstatus)) ereport(lev, /* * translator: %s is a noun phrase describing a child process, * such as "server process" */ (errmsg("%s (PID %d) exited with exit code %d", procname, pid, WEXITSTATUS(exitstatus)))); else if (WIFSIGNALED(exitstatus)) ereport(lev, /* * translator: %s is a noun phrase describing a child process, * such as "server process" */ (errmsg("%s (PID %d) was terminated by signal %d", procname, pid, WTERMSIG(exitstatus)))); else ereport(lev, /* * translator: %s is a noun phrase describing a child process, * such as "server process" */ (errmsg("%s (PID %d) exited with unexpected status %d", procname, pid, exitstatus)));}/* * Send a signal to all backend children. */static voidSignalChildren(int signal){ Dlelem *curr, *next; Backend *bp; curr = DLGetHead(BackendList); while (curr) { next = DLGetSucc(curr); bp = (Backend *) DLE_VAL(curr); if (bp->pid != MyProcPid) { ereport(DEBUG2, (errmsg_internal("sending signal %d to process %d", signal, (int) bp->pid))); kill(bp->pid, signal); } curr = next; }}/* * BackendStartup -- start backend process * * returns: STATUS_ERROR if the fork failed, STATUS_OK otherwise. */static intBackendStartup(Port *port){ Backend *bn; /* for backend cleanup */ pid_t pid;#ifdef LINUX_PROFILE struct itimerval prof_itimer;#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(); /* * Make room for backend data structure. Better before the fork() so * we can handle failure cleanly. */ bn = (Backend *) malloc(sizeof(Backend)); if (!bn) { ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); return STATUS_ERROR; } /* * 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);#ifdef LINUX_PROFILE /* * Linux's fork() resets the profiling timer in the child process. If * we want to profile child processes then we need to save and restore * the timer setting. This is a waste of time if not profiling, * however, so only do it if commanded by specific -DLINUX_PROFILE * switch. */ getitimer(ITIMER_PROF, &prof_itimer);#endif#ifdef __BEOS__ /* Specific beos actions before backend startup */ beos_before_backend_startup();#endif pid = fork(); if (pid == 0) /* child */ { int status;#ifdef LINUX_PROFILE setitimer(ITIMER_PROF, &prof_itimer, NULL);#endif#ifdef __BEOS__ /* Specific beos backend startup actions */ beos_backend_startup();#endif free(bn); status = BackendFork(port); if (status != 0) ereport(LOG, (errmsg("connection startup failed"))); proc_exit(status); } /* in parent, error */ if (pid < 0) { int save_errno = errno;#ifdef __BEOS__ /* Specific beos backend startup actions */ beos_backend_startup_failed();#endif free(bn); errno = save_errno; ereport(LOG, (errmsg("could not fork new process for connection: %m"))); report_fork_failure_to_client(port, save_errno); return STATUS_ERROR; } /* in parent, normal */ ereport(DEBUG2, (errmsg_internal("forked new backend, pid=%d socket=%d", (int) pid, port->sock))); /* * Everything's been successful, it's safe to add this backend to our * list of backends. */ bn->pid = pid; bn->cancel_key = MyCancelKey; DLAddHead(BackendList, DLNewElem(bn)); return STATUS_OK;}/* * Try to report backend fork() failure to client before we close the * connection. Since we do not care to risk blocking the postmaster on * this connection, we set the connection to non-blocking and try only once. * * This is grungy special-purpose code; we cannot use backend libpq since * it's not up and running. */static voidreport_fork_failure_to_client(Port *port, int errnum){ char buffer[1000]; /* Format the error message packet (always V2 protocol) */ snprintf(buffer, sizeof(buffer), "E%s%s\n", gettext("could not fork new process for connection: "), strerror(errnum)); /* Set port to non-blocking. Don't do send() if this fails */ if (FCNTL_NONBLOCK(port->sock) < 0) return; send(port->sock, buffer, strlen(buffer) + 1, 0);}/* * 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 voidsplit_opts(char **argv, int *argcp, char *s){ while (s && *s) { while (isspace((unsigned char) *s)) ++s; if (*s == '\0') break; argv[(*argcp)++] = s; while (*s && !isspace((unsigned char) *s)) ++s; if (*s) *s++ = '\0'; }}/* * BackendFork -- perform authentication, and if successful, 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 intBackendFork(Port *port){ char **av; int maxac; int ac; char debugbuf[32]; char protobuf[32];#ifdef EXEC_BACKEND char pbuf[NAMEDATALEN + 256];#endif int i; int status; struct timeval now; struct timezone tz; char remote_host[NI_MAXHOST]; char remote_port[NI_MAXSERV]; /* * Let's clean up ourselves as the postmaster child */ IsUnderPostmaster = true; /* we are a postmaster subprocess now */ ClientAuthInProgress = true; /* limit visibility of log messages */ /* We don't want the postmaster's proc_exit() handlers */ on_exit_reset(); /* * Signal handlers setting is moved to tcop/postgres... */ /* Close the postmaster's other sockets */ ClosePostmasterPorts(true); /* Save port etc. for ps status */ MyProcPort = port; /* Reset MyProcPid to new backend's pid */ MyProcPid = getpid(); /* * Initialize libpq and enable reporting of ereport errors to the * client. Must do this now because authentication uses libpq to send * messages. */ pq_init(); /* initialize libpq to talk to client */ whereToSendOutput = Remote; /* now safe to ereport to client */ /* * We arrange for a simple exit(0) if we receive SIGTERM or SIGQUIT * during any client authentication related communication. Otherwise * the postmaster cannot shutdown the database FAST or IMMED cleanly * if a buggy client blocks a backend during authentication. */ pqsignal(SIGTERM, authdie); pqsignal(SIGQUIT, authdie); pqsignal(SIGALRM, authdie); PG_SETMASK(&AuthBlockSig); /* * Get the remote host name and port for logging and status display. */ remote_host[0] = '\0'; remote_port[0] = '\0'; if (getnameinfo_all(&port->raddr.addr, port->raddr.salen, remote_host, sizeof(remote_host), remote_port, sizeof(remote_port), (log_hostname ? 0 : NI_NUMERICHOST) | NI_NUMERICSERV)) { getnameinfo_all(&port->raddr.addr, port->raddr.salen, remote_host, sizeof(remote_host), remote_port, sizeof(remote_port), NI_NUMERICHOST | NI_NUMERICSERV); } if (Log_connections) ereport(LOG, (errmsg("connection received: host=%s port=%s", remote_host, remote_port))); if (LogSourcePort) { /* modify remote_host for use in ps status */ char tmphost[NI_MAXHOST]; snprintf(tmphost, sizeof(tmphost), "%s:%s", remote_host, remote_port); StrNCpy(remote_host, tmphost, sizeof(remote_host)); } /* * PreAuthDelay is a debugging aid for investigating problems in the * authentication cycle: it can be set in postgresql.conf to allow * time to attach to the newly-forked backend with a debugger. (See * also the -W backend switch, which we allow clients to pass through * PGOPTIONS, but it is not honored until after authentication.) */ if (PreAuthDelay > 0) sleep(PreAuthDelay); /* * Ready to begin client interaction. We will give up and exit(0) * after a time delay, so that a broken client can't hog a connection * indefinitely. PreAuthDelay doesn't count against the time limit. */ if (!enable_sig_alarm(AuthenticationTimeout * 1000, false)) elog(FATAL, "could not set timer for authorization timeout"); /* * Receive the startup packet (which might turn out to be a cancel * request packet). */ status = ProcessStartupPacket(port, false); if (status != STATUS_OK) return 0; /* cancel request processed, or error */ /* * Now that we have the user and database name, we can set the process * title for ps. It's good to do this as early as possible in * startup. */ init_ps_display(port->user_name, port->database_name, remote_host); set_ps_display("authentication"); /* * Now perform authentication exchange. */ ClientAuthentication(port); /* might not return, if failure */ /* * Done with authentication. Disable timeout, and prevent * SIGTERM/SIGQUIT again until backend startup is complete. */ if (!disable_sig_alarm(false)) elog(FATAL, "could not disable timer for authorization timeout"); PG_SETMASK(&BlockSig); if (Log_connections) ereport(LOG, (errmsg("connection authorized: user=%s database=%s", port->user_name, port->database_name))); /* * 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((unsigned int) 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. * * The maximum possible number of commandline arguments that could come * from ExtraOptions or port->cmdline_options is (strlen + 1) / 2; see * split_opts(). * ---------------- */ maxac = 10; /* for fixed args supplied below */ maxac += (strlen(ExtraOptions) + 1) / 2; if (port->cmdline_options) maxac += (strlen(port->cmdline_options) + 1) / 2; av = (char **) MemoryContextAlloc(TopMemoryContext, maxac * sizeof(char *)); ac = 0; av[ac++] = "postgres"; /* * Pass the requested debugging level along to the backend. */ if (debug_flag > 0) { snprintf(debugbuf, sizeof(debugbuf), "-d%d", debug_flag); 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. */ snprintf(protobuf, sizeof(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";#ifdef EXEC_BACKEND Assert(UsedShmemSegID != 0 && UsedShmemSegAddr != NULL); /* database name at the end because it might contain commas */ snprintf(pbuf, NAMEDATALEN + 256, "%d,%d,%d,%p,%s", port->sock, canAcceptConnections(), UsedShmemSegID, UsedShmemSegAddr, port->database_name); av[ac++] = pbuf;#else av[ac++] = port->database_name;#endif /* * Pass the (insecure) option switches from the connection request. * (It's OK to mangle port->cmdline_options now.) */ if (port->cmdline_options) split_opts(av, &ac, port->cmdline_options); av[ac] = (char *) NULL; Assert(ac < maxac); /* * Release postmaster's working memory context so that backend can * recycle the space. Note this does not trash *MyProcPort, because * ConnCreate() allocated that space with malloc() ... else we'd need * to copy the Port data here. Also, subsidiary data such as the * username isn't lost either; see ProcessStartupPacket(). */ MemoryContextSwitchTo(TopMemoryContext
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -