diff options
author | Heikki Linnakangas | 2024-03-18 09:38:10 +0000 |
---|---|---|
committer | Heikki Linnakangas | 2024-03-18 09:38:10 +0000 |
commit | 05c3980e7f473ac2061dad9bbb7a9f0ede0279d9 (patch) | |
tree | ea797723055570851fa55f309e50d39345f72095 /src/backend/postmaster/postmaster.c | |
parent | aafc05de1bf5c0324cb5e690c6742118c1ac4af6 (diff) |
Move code for backend startup to separate file
This is code that runs in the backend process after forking, rather
than postmaster. Move it out of postmaster.c for clarity.
Reviewed-by: Tristan Partin, Andres Freund
Discussion: https://www.postgresql.org/message-id/[email protected]
Diffstat (limited to 'src/backend/postmaster/postmaster.c')
-rw-r--r-- | src/backend/postmaster/postmaster.c | 762 |
1 files changed, 4 insertions, 758 deletions
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 9a82c3dafaf..7f3170a8f06 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -95,10 +95,8 @@ #include "common/file_utils.h" #include "common/ip.h" #include "common/pg_prng.h" -#include "common/string.h" #include "lib/ilist.h" #include "libpq/libpq.h" -#include "libpq/pqformat.h" #include "libpq/pqsignal.h" #include "pg_getopt.h" #include "pgstat.h" @@ -117,13 +115,11 @@ #include "storage/ipc.h" #include "storage/pmsignal.h" #include "storage/proc.h" +#include "tcop/backend_startup.h" #include "tcop/tcopprot.h" -#include "utils/builtins.h" #include "utils/datetime.h" #include "utils/memutils.h" #include "utils/pidfile.h" -#include "utils/ps_status.h" -#include "utils/timeout.h" #include "utils/timestamp.h" #include "utils/varlena.h" @@ -382,7 +378,7 @@ static WaitEventSet *pm_wait_set; #ifdef USE_SSL /* Set when and if SSL has been initialized properly */ -static bool LoadedSSL = false; +bool LoadedSSL = false; #endif #ifdef USE_BONJOUR @@ -404,9 +400,7 @@ static void process_pm_pmsignal(void); static void process_pm_child_exit(void); static void process_pm_reload_request(void); static void process_pm_shutdown_request(void); -static void process_startup_packet_die(SIGNAL_ARGS); static void dummy_handler(SIGNAL_ARGS); -static void StartupPacketTimeoutHandler(void); static void CleanupBackend(int pid, int exitstatus); static bool CleanupBackgroundWorker(int pid, int exitstatus); static void HandleChildCrash(int pid, int exitstatus, const char *procname); @@ -414,24 +408,9 @@ static void LogChildExit(int lev, const char *procname, int pid, int exitstatus); static void PostmasterStateMachine(void); -/* Return value of canAcceptConnections() */ -typedef enum CAC_state -{ - CAC_OK, - CAC_STARTUP, - CAC_SHUTDOWN, - CAC_RECOVERY, - CAC_NOTCONSISTENT, - CAC_TOOMANY, -} CAC_state; - -static void BackendInitialize(ClientSocket *client_sock, CAC_state cac); static void ExitPostmaster(int status) pg_attribute_noreturn(); static int ServerLoop(void); static int BackendStartup(ClientSocket *client_sock); -static int ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done); -static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options); -static void processCancelRequest(Port *port, void *pkt); static void report_fork_failure_to_client(ClientSocket *client_sock, int errnum); static CAC_state canAcceptConnections(int backend_type); static bool RandomCancelKey(int32 *cancel_key); @@ -1848,411 +1827,13 @@ ServerLoop(void) } /* - * Read a client's 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.) - * - * Set ssl_done and/or gss_done when negotiation of an encrypted layer - * (currently, TLS or GSSAPI) is completed. A successful negotiation of either - * encryption layer sets both flags, but a rejected negotiation sets only the - * flag for that layer, since the client may wish to try the other one. We - * should make no assumption here about the order in which the client may make - * requests. - */ -static int -ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done) -{ - int32 len; - char *buf; - ProtocolVersion proto; - MemoryContext oldcontext; - - pq_startmsgread(); - - /* - * Grab the first byte of the length word separately, so that we can tell - * whether we have no data at all or an incomplete packet. (This might - * sound inefficient, but it's not really, because of buffering in - * pqcomm.c.) - */ - if (pq_getbytes((char *) &len, 1) == EOF) - { - /* - * If we get no data at all, don't clutter the log with a complaint; - * such cases often occur for legitimate reasons. An example is that - * we might be here after responding to NEGOTIATE_SSL_CODE, and if the - * client didn't like our response, it'll probably just drop the - * connection. Service-monitoring software also often just opens and - * closes a connection without sending anything. (So do port - * scanners, which may be less benign, but it's not really our job to - * notice those.) - */ - return STATUS_ERROR; - } - - if (pq_getbytes(((char *) &len) + 1, 3) == EOF) - { - /* Got a partial length word, so bleat about that */ - if (!ssl_done && !gss_done) - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("incomplete startup packet"))); - return STATUS_ERROR; - } - - len = pg_ntoh32(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 space to hold the startup packet, plus one extra byte that's - * initialized to be zero. This ensures we will have null termination of - * all strings inside the packet. - */ - buf = palloc(len + 1); - buf[len] = '\0'; - - if (pq_getbytes(buf, len) == EOF) - { - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("incomplete startup packet"))); - return STATUS_ERROR; - } - pq_endmsgread(); - - /* - * The first field is either a protocol version number or a special - * request code. - */ - port->proto = proto = pg_ntoh32(*((ProtocolVersion *) buf)); - - if (proto == CANCEL_REQUEST_CODE) - { - if (len != sizeof(CancelRequestPacket)) - { - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("invalid length of startup packet"))); - return STATUS_ERROR; - } - processCancelRequest(port, buf); - /* Not really an error, but we don't want to proceed further */ - return STATUS_ERROR; - } - - if (proto == NEGOTIATE_SSL_CODE && !ssl_done) - { - char SSLok; - -#ifdef USE_SSL - /* No SSL when disabled or on Unix sockets */ - if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX) - SSLok = 'N'; - else - SSLok = 'S'; /* Support for SSL */ -#else - SSLok = 'N'; /* No support for SSL */ -#endif - -retry1: - if (send(port->sock, &SSLok, 1, 0) != 1) - { - if (errno == EINTR) - goto retry1; /* if interrupted, just retry */ - 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 - - /* - * At this point we should have no data already buffered. If we do, - * it was received before we performed the SSL handshake, so it wasn't - * encrypted and indeed may have been injected by a man-in-the-middle. - * We report this case to the client. - */ - if (pq_buffer_has_data()) - ereport(FATAL, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("received unencrypted data after SSL request"), - errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack."))); - - /* - * regular startup packet, cancel, etc packet should follow, but not - * another SSL negotiation request, and a GSS request should only - * follow if SSL was rejected (client may negotiate in either order) - */ - return ProcessStartupPacket(port, true, SSLok == 'S'); - } - else if (proto == NEGOTIATE_GSS_CODE && !gss_done) - { - char GSSok = 'N'; - -#ifdef ENABLE_GSS - /* No GSSAPI encryption when on Unix socket */ - if (port->laddr.addr.ss_family != AF_UNIX) - GSSok = 'G'; -#endif - - while (send(port->sock, &GSSok, 1, 0) != 1) - { - if (errno == EINTR) - continue; - ereport(COMMERROR, - (errcode_for_socket_access(), - errmsg("failed to send GSSAPI negotiation response: %m"))); - return STATUS_ERROR; /* close the connection */ - } - -#ifdef ENABLE_GSS - if (GSSok == 'G' && secure_open_gssapi(port) == -1) - return STATUS_ERROR; -#endif - - /* - * At this point we should have no data already buffered. If we do, - * it was received before we performed the GSS handshake, so it wasn't - * encrypted and indeed may have been injected by a man-in-the-middle. - * We report this case to the client. - */ - if (pq_buffer_has_data()) - ereport(FATAL, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("received unencrypted data after GSSAPI encryption request"), - errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack."))); - - /* - * regular startup packet, cancel, etc packet should follow, but not - * another GSS negotiation request, and an SSL request should only - * follow if GSS was rejected (client may negotiate in either order) - */ - return ProcessStartupPacket(port, GSSok == 'G', 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 that the major protocol version is in range. */ - if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) || - PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(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. - */ - oldcontext = MemoryContextSwitchTo(TopMemoryContext); - - /* Handle protocol version 3 startup packet */ - { - int32 offset = sizeof(ProtocolVersion); - List *unrecognized_protocol_options = NIL; - - /* - * 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; - - while (offset < len) - { - char *nameptr = buf + offset; - int32 valoffset; - char *valptr; - - if (*nameptr == '\0') - break; /* found packet terminator */ - valoffset = offset + strlen(nameptr) + 1; - if (valoffset >= len) - break; /* missing value, will complain below */ - valptr = buf + valoffset; - - if (strcmp(nameptr, "database") == 0) - port->database_name = pstrdup(valptr); - else if (strcmp(nameptr, "user") == 0) - port->user_name = pstrdup(valptr); - else if (strcmp(nameptr, "options") == 0) - port->cmdline_options = pstrdup(valptr); - else if (strcmp(nameptr, "replication") == 0) - { - /* - * Due to backward compatibility concerns the replication - * parameter is a hybrid beast which allows the value to be - * either boolean or the string 'database'. The latter - * connects to a specific database which is e.g. required for - * logical decoding while. - */ - if (strcmp(valptr, "database") == 0) - { - am_walsender = true; - am_db_walsender = true; - } - else if (!parse_bool(valptr, &am_walsender)) - ereport(FATAL, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for parameter \"%s\": \"%s\"", - "replication", - valptr), - errhint("Valid values are: \"false\", 0, \"true\", 1, \"database\"."))); - } - else if (strncmp(nameptr, "_pq_.", 5) == 0) - { - /* - * Any option beginning with _pq_. is reserved for use as a - * protocol-level option, but at present no such options are - * defined. - */ - unrecognized_protocol_options = - lappend(unrecognized_protocol_options, pstrdup(nameptr)); - } - else - { - /* Assume it's a generic GUC option */ - port->guc_options = lappend(port->guc_options, - pstrdup(nameptr)); - port->guc_options = lappend(port->guc_options, - pstrdup(valptr)); - - /* - * Copy application_name to port if we come across it. This - * is done so we can log the application_name in the - * connection authorization message. Note that the GUC would - * be used but we haven't gone through GUC setup yet. - */ - if (strcmp(nameptr, "application_name") == 0) - { - port->application_name = pg_clean_ascii(valptr, 0); - } - } - offset = valoffset + strlen(valptr) + 1; - } - - /* - * If we didn't find a packet terminator exactly at the end of the - * given packet length, complain. - */ - if (offset != len - 1) - ereport(FATAL, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("invalid startup packet layout: expected terminator as last byte"))); - - /* - * If the client requested a newer protocol version or if the client - * requested any protocol options we didn't recognize, let them know - * the newest minor protocol version we do support and the names of - * any unrecognized options. - */ - if (PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST) || - unrecognized_protocol_options != NIL) - SendNegotiateProtocolVersion(unrecognized_protocol_options); - } - - /* Check a user name was given. */ - if (port->user_name == NULL || port->user_name[0] == '\0') - ereport(FATAL, - (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), - errmsg("no PostgreSQL user name specified in startup packet"))); - - /* The database defaults to the user name. */ - if (port->database_name == NULL || port->database_name[0] == '\0') - port->database_name = pstrdup(port->user_name); - - if (am_walsender) - MyBackendType = B_WAL_SENDER; - else - MyBackendType = B_BACKEND; - - /* - * Normal walsender backends, e.g. for streaming replication, are not - * connected to a particular database. But walsenders used for logical - * replication need to connect to a specific database. We allow streaming - * replication commands to be issued even if connected to a database as it - * can make sense to first make a basebackup and then stream changes - * starting from that. - */ - if (am_walsender && !am_db_walsender) - port->database_name[0] = '\0'; - - /* - * Done filling the Port structure - */ - MemoryContextSwitchTo(oldcontext); - - return STATUS_OK; -} - -/* - * Send a NegotiateProtocolVersion to the client. This lets the client know - * that they have requested a newer minor protocol version than we are able - * to speak. We'll speak the highest version we know about; the client can, - * of course, abandon the connection if that's a problem. - * - * We also include in the response a list of protocol options we didn't - * understand. This allows clients to include optional parameters that might - * be present either in newer protocol versions or third-party protocol - * extensions without fear of having to reconnect if those options are not - * understood, while at the same time making certain that the client is aware - * of which options were actually accepted. - */ -static void -SendNegotiateProtocolVersion(List *unrecognized_protocol_options) -{ - StringInfoData buf; - ListCell *lc; - - pq_beginmessage(&buf, PqMsg_NegotiateProtocolVersion); - pq_sendint32(&buf, PG_PROTOCOL_LATEST); - pq_sendint32(&buf, list_length(unrecognized_protocol_options)); - foreach(lc, unrecognized_protocol_options) - pq_sendstring(&buf, lfirst(lc)); - pq_endmessage(&buf); - - /* no need to flush, some other message will follow */ -} - -/* * The client has sent a cancel request packet, not a normal * start-a-new-connection packet. Perform the necessary processing. * Nothing is sent back to the client. */ -static void -processCancelRequest(Port *port, void *pkt) +void +processCancelRequest(int backendPID, int32 cancelAuthCode) { - CancelRequestPacket *canc = (CancelRequestPacket *) pkt; - int backendPID; - int32 cancelAuthCode; Backend *bp; #ifndef EXEC_BACKEND @@ -2261,9 +1842,6 @@ processCancelRequest(Port *port, void *pkt) int i; #endif - backendPID = (int) pg_ntoh32(canc->backendPID); - cancelAuthCode = (int32) pg_ntoh32(canc->cancelAuthCode); - /* * See if we have a matching backend. In the EXEC_BACKEND case, we can no * longer access the postmaster's own backend list, and must rely on the @@ -3955,12 +3533,6 @@ TerminateChildren(int signal) signal_child(SlotSyncWorkerPID, signal); } -/* Information passed from postmaster to backend process */ -typedef struct BackendStartupData -{ - CAC_state canAcceptConnections; -} BackendStartupData; - /* * BackendStartup -- start backend process * @@ -4087,302 +3659,6 @@ report_fork_failure_to_client(ClientSocket *client_sock, int errnum) } while (rc < 0 && errno == EINTR); } - -/* - * BackendInitialize -- initialize an interactive (postmaster-child) - * backend process, and collect the client's startup packet. - * - * returns: nothing. Will not return at all if there's any failure. - * - * Note: this code does not depend on having any access to shared memory. - * Indeed, our approach to SIGTERM/timeout handling *requires* that - * shared memory not have been touched yet; see comments within. - * In the EXEC_BACKEND case, we are physically attached to shared memory - * but have not yet set up most of our local pointers to shmem structures. - */ -static void -BackendInitialize(ClientSocket *client_sock, CAC_state cac) -{ - int status; - int ret; - Port *port; - char remote_host[NI_MAXHOST]; - char remote_port[NI_MAXSERV]; - StringInfoData ps_data; - MemoryContext oldcontext; - - /* Tell fd.c about the long-lived FD associated with the client_sock */ - ReserveExternalFD(); - - /* - * 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 - * PostAuthDelay, which we allow clients to pass through PGOPTIONS, but it - * is not honored until after authentication.) - */ - if (PreAuthDelay > 0) - pg_usleep(PreAuthDelay * 1000000L); - - /* This flag will remain set until InitPostgres finishes authentication */ - ClientAuthInProgress = true; /* limit visibility of log messages */ - - /* - * Initialize libpq and enable reporting of ereport errors to the client. - * Must do this now because authentication uses libpq to send messages. - * - * The Port structure and all data structures attached to it are allocated - * in TopMemoryContext, so that they survive into PostgresMain execution. - * We need not worry about leaking this storage on failure, since we - * aren't in the postmaster process anymore. - */ - oldcontext = MemoryContextSwitchTo(TopMemoryContext); - port = MyProcPort = pq_init(client_sock); - MemoryContextSwitchTo(oldcontext); - - whereToSendOutput = DestRemote; /* now safe to ereport to client */ - - /* set these to empty in case they are needed before we set them up */ - port->remote_host = ""; - port->remote_port = ""; - - /* - * We arrange to do _exit(1) if we receive SIGTERM or timeout while trying - * to collect the startup packet; while SIGQUIT results in _exit(2). - * Otherwise the postmaster cannot shutdown the database FAST or IMMED - * cleanly if a buggy client fails to send the packet promptly. - * - * Exiting with _exit(1) is only possible because we have not yet touched - * shared memory; therefore no outside-the-process state needs to get - * cleaned up. - */ - pqsignal(SIGTERM, process_startup_packet_die); - /* SIGQUIT handler was already set up by InitPostmasterChild */ - InitializeTimeouts(); /* establishes SIGALRM handler */ - sigprocmask(SIG_SETMASK, &StartupBlockSig, NULL); - - /* - * Get the remote host name and port for logging and status display. - */ - remote_host[0] = '\0'; - remote_port[0] = '\0'; - if ((ret = pg_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)) != 0) - ereport(WARNING, - (errmsg_internal("pg_getnameinfo_all() failed: %s", - gai_strerror(ret)))); - - /* - * Save remote_host and remote_port in port structure (after this, they - * will appear in log_line_prefix data for log messages). - */ - oldcontext = MemoryContextSwitchTo(TopMemoryContext); - port->remote_host = pstrdup(remote_host); - port->remote_port = pstrdup(remote_port); - - /* And now we can issue the Log_connections message, if wanted */ - if (Log_connections) - { - if (remote_port[0]) - ereport(LOG, - (errmsg("connection received: host=%s port=%s", - remote_host, - remote_port))); - else - ereport(LOG, - (errmsg("connection received: host=%s", - remote_host))); - } - - /* - * If we did a reverse lookup to name, we might as well save the results - * rather than possibly repeating the lookup during authentication. - * - * Note that we don't want to specify NI_NAMEREQD above, because then we'd - * get nothing useful for a client without an rDNS entry. Therefore, we - * must check whether we got a numeric IPv4 or IPv6 address, and not save - * it into remote_hostname if so. (This test is conservative and might - * sometimes classify a hostname as numeric, but an error in that - * direction is safe; it only results in a possible extra lookup.) - */ - if (log_hostname && - ret == 0 && - strspn(remote_host, "0123456789.") < strlen(remote_host) && - strspn(remote_host, "0123456789ABCDEFabcdef:") < strlen(remote_host)) - { - port->remote_hostname = pstrdup(remote_host); - } - MemoryContextSwitchTo(oldcontext); - - /* - * Ready to begin client interaction. We will give up and _exit(1) after - * a time delay, so that a broken client can't hog a connection - * indefinitely. PreAuthDelay and any DNS interactions above don't count - * against the time limit. - * - * Note: AuthenticationTimeout is applied here while waiting for the - * startup packet, and then again in InitPostgres for the duration of any - * authentication operations. So a hostile client could tie up the - * process for nearly twice AuthenticationTimeout before we kick him off. - * - * Note: because PostgresMain will call InitializeTimeouts again, the - * registration of STARTUP_PACKET_TIMEOUT will be lost. This is okay - * since we never use it again after this function. - */ - RegisterTimeout(STARTUP_PACKET_TIMEOUT, StartupPacketTimeoutHandler); - enable_timeout_after(STARTUP_PACKET_TIMEOUT, AuthenticationTimeout * 1000); - - /* - * Receive the startup packet (which might turn out to be a cancel request - * packet). - */ - status = ProcessStartupPacket(port, false, false); - - /* - * If we're going to reject the connection due to database state, say so - * now instead of wasting cycles on an authentication exchange. (This also - * allows a pg_ping utility to be written.) - */ - if (status == STATUS_OK) - { - switch (cac) - { - case CAC_STARTUP: - ereport(FATAL, - (errcode(ERRCODE_CANNOT_CONNECT_NOW), - errmsg("the database system is starting up"))); - break; - case CAC_NOTCONSISTENT: - if (EnableHotStandby) - ereport(FATAL, - (errcode(ERRCODE_CANNOT_CONNECT_NOW), - errmsg("the database system is not yet accepting connections"), - errdetail("Consistent recovery state has not been yet reached."))); - else - ereport(FATAL, - (errcode(ERRCODE_CANNOT_CONNECT_NOW), - errmsg("the database system is not accepting connections"), - errdetail("Hot standby mode is disabled."))); - break; - case CAC_SHUTDOWN: - ereport(FATAL, - (errcode(ERRCODE_CANNOT_CONNECT_NOW), - errmsg("the database system is shutting down"))); - break; - case CAC_RECOVERY: - ereport(FATAL, - (errcode(ERRCODE_CANNOT_CONNECT_NOW), - errmsg("the database system is in recovery mode"))); - break; - case CAC_TOOMANY: - ereport(FATAL, - (errcode(ERRCODE_TOO_MANY_CONNECTIONS), - errmsg("sorry, too many clients already"))); - break; - case CAC_OK: - break; - } - } - - /* - * Disable the timeout, and prevent SIGTERM again. - */ - disable_timeout(STARTUP_PACKET_TIMEOUT, false); - sigprocmask(SIG_SETMASK, &BlockSig, NULL); - - /* - * As a safety check that nothing in startup has yet performed - * shared-memory modifications that would need to be undone if we had - * exited through SIGTERM or timeout above, check that no on_shmem_exit - * handlers have been registered yet. (This isn't terribly bulletproof, - * since someone might misuse an on_proc_exit handler for shmem cleanup, - * but it's a cheap and helpful check. We cannot disallow on_proc_exit - * handlers unfortunately, since pq_init() already registered one.) - */ - check_on_shmem_exit_lists_are_empty(); - - /* - * Stop here if it was bad or a cancel packet. ProcessStartupPacket - * already did any appropriate error reporting. - */ - if (status != STATUS_OK) - proc_exit(0); - - /* - * 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. - */ - initStringInfo(&ps_data); - if (am_walsender) - appendStringInfo(&ps_data, "%s ", GetBackendTypeDesc(B_WAL_SENDER)); - appendStringInfo(&ps_data, "%s ", port->user_name); - if (port->database_name[0] != '\0') - appendStringInfo(&ps_data, "%s ", port->database_name); - appendStringInfoString(&ps_data, port->remote_host); - if (port->remote_port[0] != '\0') - appendStringInfo(&ps_data, "(%s)", port->remote_port); - - init_ps_display(ps_data.data); - pfree(ps_data.data); - - set_ps_display("initializing"); -} - -void -BackendMain(char *startup_data, size_t startup_data_len) -{ - BackendStartupData *bsdata = (BackendStartupData *) startup_data; - - Assert(startup_data_len == sizeof(BackendStartupData)); - Assert(MyClientSocket != NULL); - -#ifdef EXEC_BACKEND - - /* - * Need to reinitialize the SSL library in the backend, since the context - * structures contain function pointers and cannot be passed through the - * parameter file. - * - * If for some reason reload fails (maybe the user installed broken key - * files), soldier on without SSL; that's better than all connections - * becoming impossible. - * - * XXX should we do this in all child processes? For the moment it's - * enough to do it in backend children. - */ -#ifdef USE_SSL - if (EnableSSL) - { - if (secure_initialize(false) == 0) - LoadedSSL = true; - else - ereport(LOG, - (errmsg("SSL configuration could not be loaded in child process"))); - } -#endif -#endif - - /* Perform additional initialization and collect startup packet */ - BackendInitialize(MyClientSocket, bsdata->canAcceptConnections); - - /* - * Create a per-backend PGPROC struct in shared memory. We must do this - * before we can use LWLocks or access any shared memory. - */ - InitProcess(); - - /* - * Make sure we aren't in PostmasterContext anymore. (We can't delete it - * just yet, though, because InitPostgres will need the HBA data.) - */ - MemoryContextSwitchTo(TopMemoryContext); - - PostgresMain(MyProcPort->database_name, MyProcPort->user_name); -} - - /* * ExitPostmaster -- cleanup * @@ -4572,25 +3848,6 @@ process_pm_pmsignal(void) } /* - * SIGTERM while processing startup packet. - * - * Running proc_exit() from a signal handler would be quite unsafe. - * However, since we have not yet touched shared memory, we can just - * pull the plug and exit without running any atexit handlers. - * - * One might be tempted to try to send a message, or log one, indicating - * why we are disconnecting. However, that would be quite unsafe in itself. - * Also, it seems undesirable to provide clues about the database's state - * to a client that has not yet completed authentication, or even sent us - * a startup packet. - */ -static void -process_startup_packet_die(SIGNAL_ARGS) -{ - _exit(1); -} - -/* * Dummy signal handler * * We use this for signals that we don't actually use in the postmaster, @@ -4605,17 +3862,6 @@ dummy_handler(SIGNAL_ARGS) } /* - * Timeout while processing startup packet. - * As for process_startup_packet_die(), we exit via _exit(1). - */ -static void -StartupPacketTimeoutHandler(void) -{ - _exit(1); -} - - -/* * Generate a random cancel key. */ static bool |