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/7a59b073-5b5b-151e-7ed3-8b01ff7ce9ef@iki.fi
This commit is contained in:
parent
aafc05de1b
commit
05c3980e7f
|
@ -58,6 +58,7 @@
|
|||
#include "storage/pg_shmem.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"
|
||||
|
|
|
@ -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);
|
||||
|
@ -1847,412 +1826,14 @@ 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
|
||||
*
|
||||
|
@ -4571,25 +3847,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
|
||||
*
|
||||
|
@ -4604,17 +3861,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.
|
||||
*/
|
||||
|
|
|
@ -13,6 +13,7 @@ top_builddir = ../../..
|
|||
include $(top_builddir)/src/Makefile.global
|
||||
|
||||
OBJS = \
|
||||
backend_startup.o \
|
||||
cmdtag.o \
|
||||
dest.o \
|
||||
fastpath.o \
|
||||
|
|
|
@ -0,0 +1,778 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* backend_startup.c
|
||||
* Backend startup code
|
||||
*
|
||||
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/backend/tcop/backend_startup.c
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "access/xlog.h"
|
||||
#include "common/ip.h"
|
||||
#include "common/string.h"
|
||||
#include "libpq/libpq.h"
|
||||
#include "libpq/libpq-be.h"
|
||||
#include "libpq/pqformat.h"
|
||||
#include "libpq/pqsignal.h"
|
||||
#include "miscadmin.h"
|
||||
#include "postmaster/postmaster.h"
|
||||
#include "replication/walsender.h"
|
||||
#include "storage/fd.h"
|
||||
#include "storage/ipc.h"
|
||||
#include "storage/proc.h"
|
||||
#include "tcop/backend_startup.h"
|
||||
#include "tcop/tcopprot.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/ps_status.h"
|
||||
#include "utils/timeout.h"
|
||||
|
||||
static void BackendInitialize(ClientSocket *client_sock, CAC_state cac);
|
||||
static int ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done);
|
||||
static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
|
||||
static void process_startup_packet_die(SIGNAL_ARGS);
|
||||
static void StartupPacketTimeoutHandler(void);
|
||||
|
||||
/*
|
||||
* Entry point for a new backend process.
|
||||
*
|
||||
* Initialize the connection, read the startup packet, authenticate the
|
||||
* client, and start the main processing loop.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
CancelRequestPacket *canc;
|
||||
int backendPID;
|
||||
int32 cancelAuthCode;
|
||||
|
||||
if (len != sizeof(CancelRequestPacket))
|
||||
{
|
||||
ereport(COMMERROR,
|
||||
(errcode(ERRCODE_PROTOCOL_VIOLATION),
|
||||
errmsg("invalid length of startup packet")));
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
canc = (CancelRequestPacket *) buf;
|
||||
backendPID = (int) pg_ntoh32(canc->backendPID);
|
||||
cancelAuthCode = (int32) pg_ntoh32(canc->cancelAuthCode);
|
||||
|
||||
processCancelRequest(backendPID, cancelAuthCode);
|
||||
/* 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 */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Timeout while processing startup packet.
|
||||
* As for process_startup_packet_die(), we exit via _exit(1).
|
||||
*/
|
||||
static void
|
||||
StartupPacketTimeoutHandler(void)
|
||||
{
|
||||
_exit(1);
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright (c) 2022-2024, PostgreSQL Global Development Group
|
||||
|
||||
backend_sources += files(
|
||||
'backend_startup.c',
|
||||
'cmdtag.c',
|
||||
'dest.c',
|
||||
'fastpath.c',
|
||||
|
|
|
@ -52,6 +52,8 @@ extern PGDLLIMPORT int postmaster_alive_fds[2];
|
|||
|
||||
extern PGDLLIMPORT const char *progname;
|
||||
|
||||
extern bool LoadedSSL;
|
||||
|
||||
extern void PostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
|
||||
extern void ClosePostmasterPorts(bool am_syslogger);
|
||||
extern void InitProcessGlobals(void);
|
||||
|
@ -60,7 +62,7 @@ extern int MaxLivePostmasterChildren(void);
|
|||
|
||||
extern bool PostmasterMarkPIDForWorkerNotify(int);
|
||||
|
||||
extern void BackendMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
|
||||
extern void processCancelRequest(int backendPID, int32 cancelAuthCode);
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
extern Size ShmemBackendArraySize(void);
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* backend_startup.h
|
||||
* prototypes for backend_startup.c.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* src/include/tcop/backend_startup.h
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef BACKEND_STARTUP_H
|
||||
#define BACKEND_STARTUP_H
|
||||
|
||||
/*
|
||||
* CAC_state is passed from postmaster to the backend process, to indicate
|
||||
* whether the connection should be accepted, or if the process should just
|
||||
* send an error to the client and close the connection. Note that the
|
||||
* connection can fail for various reasons even if postmaster passed CAC_OK.
|
||||
*/
|
||||
typedef enum CAC_state
|
||||
{
|
||||
CAC_OK,
|
||||
CAC_STARTUP,
|
||||
CAC_SHUTDOWN,
|
||||
CAC_RECOVERY,
|
||||
CAC_NOTCONSISTENT,
|
||||
CAC_TOOMANY,
|
||||
} CAC_state;
|
||||
|
||||
/* Information passed from postmaster to backend process in 'startup_data' */
|
||||
typedef struct BackendStartupData
|
||||
{
|
||||
CAC_state canAcceptConnections;
|
||||
} BackendStartupData;
|
||||
|
||||
extern void BackendMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
|
||||
|
||||
#endif /* BACKEND_STARTUP_H */
|
Loading…
Reference in New Issue