📄 postmaster.c
字号:
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(); /* * Set up signal handlers for the postmaster process. */ pqsignal(SIGHUP, pmdie); /* send SIGHUP, don't die */ pqsignal(SIGINT, pmdie); /* die */ pqsignal(SIGQUIT, pmdie); /* send SIGTERM and die */ pqsignal(SIGTERM, pmdie); /* send SIGTERM,SIGKILL and die */ pqsignal(SIGPIPE, SIG_IGN); /* ignored */ pqsignal(SIGUSR1, pmdie); /* send SIGUSR1 and die */ 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 */ status = ServerLoop(); ExitPostmaster(status != STATUS_OK); return 0; /* not reached */}static voidpmdaemonize(void){ int i; if (fork()) _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 voidusage(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|2|3]\tset debugging level\n"); fprintf(stderr, "\t-i \t\tlisten on TCP/IP sockets as well as Unix domain socket\n"); 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 intServerLoop(void){ fd_set readmask, writemask; int nSockets; Dlelem *curr; struct timeval now, later; struct timezone tz; gettimeofday(&now, &tz); nSockets = initMasks(&readmask, &writemask);#ifdef HAVE_SIGPROCMASK sigprocmask(0, NULL, &oldsigmask); sigemptyset(&newsigmask); sigaddset(&newsigmask, SIGCHLD);#endif for (;;) { Port *port; fd_set rmask, wmask;#ifdef HAVE_SIGPROCMASK sigprocmask(SIG_SETMASK, &oldsigmask, 0);#else sigsetmask(orgsigmask);#endif memmove((char *) &rmask, (char *) &readmask, sizeof(fd_set)); memmove((char *) &wmask, (char *) &writemask, sizeof(fd_set)); 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)); } /* * [TRH] To avoid race conditions, block SIGCHLD signals while we * are handling the request. (both reaper() and ConnCreate() * manipulate the BackEnd list, and reaper() calls free() which is * usually non-reentrant.) */#ifdef HAVE_SIGPROCMASK sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask);#else sigblock(sigmask(SIGCHLD)); /* XXX[TRH] portability */#endif /* 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; if (FD_ISSET(port->sock, &rmask)) { if (DebugLvl > 1) fprintf(stderr, "%s: ServerLoop:\t\thandling reading %d\n", progname, port->sock); if (PacketReceiveFragment(&port->pktInfo, port->sock) != 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->pktInfo, port->sock) != 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. */ 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); free(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 intinitMasks(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 intreadStartupPacket(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); /* Could add additional special packet types here */ /* 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 intprocessCancelRequest(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); 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;}/* * reset_shared -- reset shared memory and semaphores */static voidreset_shared(unsigned short port){ ipc_key = port * 1000 + shmem_seq * 100; CreateSharedMemoryAndSemaphores(ipc_key, MaxBackends); ActiveBackends = FALSE; shmem_seq += 1; if (shmem_seq >= 10) shmem_seq -= 10;}/* * pmdie -- signal handler for cleaning up after a kill signal. */static voidpmdie(SIGNAL_ARGS){ int i; TPRINTF(TRACE_VERBOSE, "pmdie %d", postgres_signal_arg); /* * Kill self and/or children processes depending on signal number. */ switch (postgres_signal_arg) { case SIGHUP: /* Send SIGHUP to all children (update options flags) */ SignalChildren(SIGHUP); /* Don't die */ return; case SIGINT: /* Die without killing children */ break; case SIGQUIT: /* Shutdown all children with SIGTERM */ SignalChildren(SIGTERM); /* Don't die */ return; case SIGTERM: /* Shutdown all children with SIGTERM and SIGKILL, then die */ SignalChildren(SIGTERM); for (i = 0; i < 10; i++) { if (!DLGetHead(BackendList)) break; sleep(1); } if (DLGetHead(BackendList)) SignalChildren(SIGKILL); break; case SIGUSR1: /* Quick die all children with SIGUSR1 and die */ SignalChildren(SIGUSR1); break; case SIGUSR2: /* Send SIGUSR2 to all children (AsyncNotifyHandler) */ SignalChildren(SIGUSR2); /* Don't die */ return; } /* exit postmaster */ proc_exit(0);}/* * Reaper -- signal handler to cleanup after a backend (child) dies. */static voidreaper(SIGNAL_ARGS){/* GH: replace waitpid for !HAVE_WAITPID. Does this work ? */#ifdef HAVE_WAITPID int status; /* backend exit status */#else union wait statusp; /* backend exit status */#endif int pid; /* process id of dead backend */ if (DebugLvl) fprintf(stderr, "%s: reaping dead processes...\n", progname);#ifdef HAVE_WAITPID while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { CleanupProc(pid, status); pqsignal(SIGCHLD, reaper); }#else while ((pid = wait3(&statusp, WNOHANG, NULL)) > 0) { CleanupProc(pid, statusp.w_status); pqsignal(SIGCHLD, reaper); }#endif}/* * 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 voidCleanupProc(int pid,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -