LCOV - code coverage report
Current view: top level - src/backend/tcop - backend_startup.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 220 289 76.1 %
Date: 2025-08-17 01:17:32 Functions: 8 11 72.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * backend_startup.c
       4             :  *    Backend startup code
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/tcop/backend_startup.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : 
      16             : #include "postgres.h"
      17             : 
      18             : #include <unistd.h>
      19             : 
      20             : #include "access/xlog.h"
      21             : #include "access/xlogrecovery.h"
      22             : #include "common/ip.h"
      23             : #include "common/string.h"
      24             : #include "libpq/libpq.h"
      25             : #include "libpq/libpq-be.h"
      26             : #include "libpq/pqformat.h"
      27             : #include "libpq/pqsignal.h"
      28             : #include "miscadmin.h"
      29             : #include "postmaster/postmaster.h"
      30             : #include "replication/walsender.h"
      31             : #include "storage/fd.h"
      32             : #include "storage/ipc.h"
      33             : #include "storage/procsignal.h"
      34             : #include "storage/proc.h"
      35             : #include "tcop/backend_startup.h"
      36             : #include "tcop/tcopprot.h"
      37             : #include "utils/builtins.h"
      38             : #include "utils/guc_hooks.h"
      39             : #include "utils/injection_point.h"
      40             : #include "utils/memutils.h"
      41             : #include "utils/ps_status.h"
      42             : #include "utils/timeout.h"
      43             : #include "utils/varlena.h"
      44             : 
      45             : /* GUCs */
      46             : bool        Trace_connection_negotiation = false;
      47             : uint32      log_connections = 0;
      48             : char       *log_connections_string = NULL;
      49             : 
      50             : /* Other globals */
      51             : 
      52             : /*
      53             :  * ConnectionTiming stores timestamps of various points in connection
      54             :  * establishment and setup.
      55             :  * ready_for_use is initialized to a special value here so we can check if
      56             :  * we've already set it before doing so in PostgresMain().
      57             :  */
      58             : ConnectionTiming conn_timing = {.ready_for_use = TIMESTAMP_MINUS_INFINITY};
      59             : 
      60             : static void BackendInitialize(ClientSocket *client_sock, CAC_state cac);
      61             : static int  ProcessSSLStartup(Port *port);
      62             : static int  ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done);
      63             : static void ProcessCancelRequestPacket(Port *port, void *pkt, int pktlen);
      64             : static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
      65             : static void process_startup_packet_die(SIGNAL_ARGS);
      66             : static void StartupPacketTimeoutHandler(void);
      67             : static bool validate_log_connections_options(List *elemlist, uint32 *flags);
      68             : 
      69             : /*
      70             :  * Entry point for a new backend process.
      71             :  *
      72             :  * Initialize the connection, read the startup packet, authenticate the
      73             :  * client, and start the main processing loop.
      74             :  */
      75             : void
      76       26142 : BackendMain(const void *startup_data, size_t startup_data_len)
      77             : {
      78       26142 :     const BackendStartupData *bsdata = startup_data;
      79             : 
      80             :     Assert(startup_data_len == sizeof(BackendStartupData));
      81             :     Assert(MyClientSocket != NULL);
      82             : 
      83             : #ifdef EXEC_BACKEND
      84             : 
      85             :     /*
      86             :      * Need to reinitialize the SSL library in the backend, since the context
      87             :      * structures contain function pointers and cannot be passed through the
      88             :      * parameter file.
      89             :      *
      90             :      * If for some reason reload fails (maybe the user installed broken key
      91             :      * files), soldier on without SSL; that's better than all connections
      92             :      * becoming impossible.
      93             :      *
      94             :      * XXX should we do this in all child processes?  For the moment it's
      95             :      * enough to do it in backend children.
      96             :      */
      97             : #ifdef USE_SSL
      98             :     if (EnableSSL)
      99             :     {
     100             :         if (secure_initialize(false) == 0)
     101             :             LoadedSSL = true;
     102             :         else
     103             :             ereport(LOG,
     104             :                     (errmsg("SSL configuration could not be loaded in child process")));
     105             :     }
     106             : #endif
     107             : #endif
     108             : 
     109             :     /* Perform additional initialization and collect startup packet */
     110       26142 :     BackendInitialize(MyClientSocket, bsdata->canAcceptConnections);
     111             : 
     112             :     /*
     113             :      * Create a per-backend PGPROC struct in shared memory.  We must do this
     114             :      * before we can use LWLocks or access any shared memory.
     115             :      */
     116       25516 :     InitProcess();
     117             : 
     118             :     /*
     119             :      * Make sure we aren't in PostmasterContext anymore.  (We can't delete it
     120             :      * just yet, though, because InitPostgres will need the HBA data.)
     121             :      */
     122       25510 :     MemoryContextSwitchTo(TopMemoryContext);
     123             : 
     124       25510 :     PostgresMain(MyProcPort->database_name, MyProcPort->user_name);
     125             : }
     126             : 
     127             : 
     128             : /*
     129             :  * BackendInitialize -- initialize an interactive (postmaster-child)
     130             :  *              backend process, and collect the client's startup packet.
     131             :  *
     132             :  * returns: nothing.  Will not return at all if there's any failure.
     133             :  *
     134             :  * Note: this code does not depend on having any access to shared memory.
     135             :  * Indeed, our approach to SIGTERM/timeout handling *requires* that
     136             :  * shared memory not have been touched yet; see comments within.
     137             :  * In the EXEC_BACKEND case, we are physically attached to shared memory
     138             :  * but have not yet set up most of our local pointers to shmem structures.
     139             :  */
     140             : static void
     141       26142 : BackendInitialize(ClientSocket *client_sock, CAC_state cac)
     142             : {
     143             :     int         status;
     144             :     int         ret;
     145             :     Port       *port;
     146             :     char        remote_host[NI_MAXHOST];
     147             :     char        remote_port[NI_MAXSERV];
     148             :     StringInfoData ps_data;
     149             :     MemoryContext oldcontext;
     150             : 
     151             :     /* Tell fd.c about the long-lived FD associated with the client_sock */
     152       26142 :     ReserveExternalFD();
     153             : 
     154             :     /*
     155             :      * PreAuthDelay is a debugging aid for investigating problems in the
     156             :      * authentication cycle: it can be set in postgresql.conf to allow time to
     157             :      * attach to the newly-forked backend with a debugger.  (See also
     158             :      * PostAuthDelay, which we allow clients to pass through PGOPTIONS, but it
     159             :      * is not honored until after authentication.)
     160             :      */
     161       26142 :     if (PreAuthDelay > 0)
     162           0 :         pg_usleep(PreAuthDelay * 1000000L);
     163             : 
     164             :     /* This flag will remain set until InitPostgres finishes authentication */
     165       26142 :     ClientAuthInProgress = true;    /* limit visibility of log messages */
     166             : 
     167             :     /*
     168             :      * Initialize libpq and enable reporting of ereport errors to the client.
     169             :      * Must do this now because authentication uses libpq to send messages.
     170             :      *
     171             :      * The Port structure and all data structures attached to it are allocated
     172             :      * in TopMemoryContext, so that they survive into PostgresMain execution.
     173             :      * We need not worry about leaking this storage on failure, since we
     174             :      * aren't in the postmaster process anymore.
     175             :      */
     176       26142 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     177       26142 :     port = MyProcPort = pq_init(client_sock);
     178       26142 :     MemoryContextSwitchTo(oldcontext);
     179             : 
     180       26142 :     whereToSendOutput = DestRemote; /* now safe to ereport to client */
     181             : 
     182             :     /* set these to empty in case they are needed before we set them up */
     183       26142 :     port->remote_host = "";
     184       26142 :     port->remote_port = "";
     185             : 
     186             :     /*
     187             :      * We arrange to do _exit(1) if we receive SIGTERM or timeout while trying
     188             :      * to collect the startup packet; while SIGQUIT results in _exit(2).
     189             :      * Otherwise the postmaster cannot shutdown the database FAST or IMMED
     190             :      * cleanly if a buggy client fails to send the packet promptly.
     191             :      *
     192             :      * Exiting with _exit(1) is only possible because we have not yet touched
     193             :      * shared memory; therefore no outside-the-process state needs to get
     194             :      * cleaned up.
     195             :      */
     196       26142 :     pqsignal(SIGTERM, process_startup_packet_die);
     197             :     /* SIGQUIT handler was already set up by InitPostmasterChild */
     198       26142 :     InitializeTimeouts();       /* establishes SIGALRM handler */
     199       26142 :     sigprocmask(SIG_SETMASK, &StartupBlockSig, NULL);
     200             : 
     201             :     /*
     202             :      * Get the remote host name and port for logging and status display.
     203             :      */
     204       26142 :     remote_host[0] = '\0';
     205       26142 :     remote_port[0] = '\0';
     206       26142 :     if ((ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
     207             :                                   remote_host, sizeof(remote_host),
     208             :                                   remote_port, sizeof(remote_port),
     209             :                                   (log_hostname ? 0 : NI_NUMERICHOST) | NI_NUMERICSERV)) != 0)
     210           0 :         ereport(WARNING,
     211             :                 (errmsg_internal("pg_getnameinfo_all() failed: %s",
     212             :                                  gai_strerror(ret))));
     213             : 
     214             :     /*
     215             :      * Save remote_host and remote_port in port structure (after this, they
     216             :      * will appear in log_line_prefix data for log messages).
     217             :      */
     218       26142 :     port->remote_host = MemoryContextStrdup(TopMemoryContext, remote_host);
     219       26142 :     port->remote_port = MemoryContextStrdup(TopMemoryContext, remote_port);
     220             : 
     221             :     /* And now we can log that the connection was received, if enabled */
     222       26142 :     if (log_connections & LOG_CONNECTION_RECEIPT)
     223             :     {
     224         784 :         if (remote_port[0])
     225         292 :             ereport(LOG,
     226             :                     (errmsg("connection received: host=%s port=%s",
     227             :                             remote_host,
     228             :                             remote_port)));
     229             :         else
     230         492 :             ereport(LOG,
     231             :                     (errmsg("connection received: host=%s",
     232             :                             remote_host)));
     233             :     }
     234             : 
     235             :     /* For testing client error handling */
     236             : #ifdef USE_INJECTION_POINTS
     237       26142 :     INJECTION_POINT("backend-initialize", NULL);
     238       26142 :     if (IS_INJECTION_POINT_ATTACHED("backend-initialize-v2-error"))
     239             :     {
     240             :         /*
     241             :          * This simulates an early error from a pre-v14 server, which used the
     242             :          * version 2 protocol for any errors that occurred before processing
     243             :          * the startup packet.
     244             :          */
     245           0 :         FrontendProtocol = PG_PROTOCOL(2, 0);
     246           0 :         elog(FATAL, "protocol version 2 error triggered");
     247             :     }
     248             : #endif
     249             : 
     250             :     /*
     251             :      * If we did a reverse lookup to name, we might as well save the results
     252             :      * rather than possibly repeating the lookup during authentication.
     253             :      *
     254             :      * Note that we don't want to specify NI_NAMEREQD above, because then we'd
     255             :      * get nothing useful for a client without an rDNS entry.  Therefore, we
     256             :      * must check whether we got a numeric IPv4 or IPv6 address, and not save
     257             :      * it into remote_hostname if so.  (This test is conservative and might
     258             :      * sometimes classify a hostname as numeric, but an error in that
     259             :      * direction is safe; it only results in a possible extra lookup.)
     260             :      */
     261       26142 :     if (log_hostname &&
     262         234 :         ret == 0 &&
     263         234 :         strspn(remote_host, "0123456789.") < strlen(remote_host) &&
     264         234 :         strspn(remote_host, "0123456789ABCDEFabcdef:") < strlen(remote_host))
     265             :     {
     266         234 :         port->remote_hostname = MemoryContextStrdup(TopMemoryContext, remote_host);
     267             :     }
     268             : 
     269             :     /*
     270             :      * Ready to begin client interaction.  We will give up and _exit(1) after
     271             :      * a time delay, so that a broken client can't hog a connection
     272             :      * indefinitely.  PreAuthDelay and any DNS interactions above don't count
     273             :      * against the time limit.
     274             :      *
     275             :      * Note: AuthenticationTimeout is applied here while waiting for the
     276             :      * startup packet, and then again in InitPostgres for the duration of any
     277             :      * authentication operations.  So a hostile client could tie up the
     278             :      * process for nearly twice AuthenticationTimeout before we kick him off.
     279             :      *
     280             :      * Note: because PostgresMain will call InitializeTimeouts again, the
     281             :      * registration of STARTUP_PACKET_TIMEOUT will be lost.  This is okay
     282             :      * since we never use it again after this function.
     283             :      */
     284       26142 :     RegisterTimeout(STARTUP_PACKET_TIMEOUT, StartupPacketTimeoutHandler);
     285       26142 :     enable_timeout_after(STARTUP_PACKET_TIMEOUT, AuthenticationTimeout * 1000);
     286             : 
     287             :     /* Handle direct SSL handshake */
     288       26142 :     status = ProcessSSLStartup(port);
     289             : 
     290             :     /*
     291             :      * Receive the startup packet (which might turn out to be a cancel request
     292             :      * packet).
     293             :      */
     294       26142 :     if (status == STATUS_OK)
     295       26134 :         status = ProcessStartupPacket(port, false, false);
     296             : 
     297             :     /*
     298             :      * If we're going to reject the connection due to database state, say so
     299             :      * now instead of wasting cycles on an authentication exchange. (This also
     300             :      * allows a pg_ping utility to be written.)
     301             :      */
     302       26140 :     if (status == STATUS_OK)
     303             :     {
     304       26032 :         switch (cac)
     305             :         {
     306         412 :             case CAC_STARTUP:
     307         412 :                 ereport(FATAL,
     308             :                         (errcode(ERRCODE_CANNOT_CONNECT_NOW),
     309             :                          errmsg("the database system is starting up")));
     310             :                 break;
     311          18 :             case CAC_NOTHOTSTANDBY:
     312          18 :                 if (!EnableHotStandby)
     313           0 :                     ereport(FATAL,
     314             :                             (errcode(ERRCODE_CANNOT_CONNECT_NOW),
     315             :                              errmsg("the database system is not accepting connections"),
     316             :                              errdetail("Hot standby mode is disabled.")));
     317          18 :                 else if (reachedConsistency)
     318           0 :                     ereport(FATAL,
     319             :                             (errcode(ERRCODE_CANNOT_CONNECT_NOW),
     320             :                              errmsg("the database system is not yet accepting connections"),
     321             :                              errdetail("Recovery snapshot is not yet ready for hot standby."),
     322             :                              errhint("To enable hot standby, close write transactions with more than %d subtransactions on the primary server.",
     323             :                                      PGPROC_MAX_CACHED_SUBXIDS)));
     324             :                 else
     325          18 :                     ereport(FATAL,
     326             :                             (errcode(ERRCODE_CANNOT_CONNECT_NOW),
     327             :                              errmsg("the database system is not yet accepting connections"),
     328             :                              errdetail("Consistent recovery state has not been yet reached.")));
     329             :                 break;
     330          78 :             case CAC_SHUTDOWN:
     331          78 :                 ereport(FATAL,
     332             :                         (errcode(ERRCODE_CANNOT_CONNECT_NOW),
     333             :                          errmsg("the database system is shutting down")));
     334             :                 break;
     335           6 :             case CAC_RECOVERY:
     336           6 :                 ereport(FATAL,
     337             :                         (errcode(ERRCODE_CANNOT_CONNECT_NOW),
     338             :                          errmsg("the database system is in recovery mode")));
     339             :                 break;
     340           2 :             case CAC_TOOMANY:
     341           2 :                 ereport(FATAL,
     342             :                         (errcode(ERRCODE_TOO_MANY_CONNECTIONS),
     343             :                          errmsg("sorry, too many clients already")));
     344             :                 break;
     345       25516 :             case CAC_OK:
     346       25516 :                 break;
     347             :         }
     348             :     }
     349             : 
     350             :     /*
     351             :      * Disable the timeout, and prevent SIGTERM again.
     352             :      */
     353       25624 :     disable_timeout(STARTUP_PACKET_TIMEOUT, false);
     354       25624 :     sigprocmask(SIG_SETMASK, &BlockSig, NULL);
     355             : 
     356             :     /*
     357             :      * As a safety check that nothing in startup has yet performed
     358             :      * shared-memory modifications that would need to be undone if we had
     359             :      * exited through SIGTERM or timeout above, check that no on_shmem_exit
     360             :      * handlers have been registered yet.  (This isn't terribly bulletproof,
     361             :      * since someone might misuse an on_proc_exit handler for shmem cleanup,
     362             :      * but it's a cheap and helpful check.  We cannot disallow on_proc_exit
     363             :      * handlers unfortunately, since pq_init() already registered one.)
     364             :      */
     365       25624 :     check_on_shmem_exit_lists_are_empty();
     366             : 
     367             :     /*
     368             :      * Stop here if it was bad or a cancel packet.  ProcessStartupPacket
     369             :      * already did any appropriate error reporting.
     370             :      */
     371       25624 :     if (status != STATUS_OK)
     372         108 :         proc_exit(0);
     373             : 
     374             :     /*
     375             :      * Now that we have the user and database name, we can set the process
     376             :      * title for ps.  It's good to do this as early as possible in startup.
     377             :      */
     378       25516 :     initStringInfo(&ps_data);
     379       25516 :     if (am_walsender)
     380        2270 :         appendStringInfo(&ps_data, "%s ", GetBackendTypeDesc(B_WAL_SENDER));
     381       25516 :     appendStringInfo(&ps_data, "%s ", port->user_name);
     382       25516 :     if (port->database_name[0] != '\0')
     383       24604 :         appendStringInfo(&ps_data, "%s ", port->database_name);
     384       25516 :     appendStringInfoString(&ps_data, port->remote_host);
     385       25516 :     if (port->remote_port[0] != '\0')
     386         504 :         appendStringInfo(&ps_data, "(%s)", port->remote_port);
     387             : 
     388       25516 :     init_ps_display(ps_data.data);
     389       25516 :     pfree(ps_data.data);
     390             : 
     391       25516 :     set_ps_display("initializing");
     392       25516 : }
     393             : 
     394             : /*
     395             :  * Check for a direct SSL connection.
     396             :  *
     397             :  * This happens before the startup packet so we are careful not to actually
     398             :  * read any bytes from the stream if it's not a direct SSL connection.
     399             :  */
     400             : static int
     401       26142 : ProcessSSLStartup(Port *port)
     402             : {
     403             :     int         firstbyte;
     404             : 
     405             :     Assert(!port->ssl_in_use);
     406             : 
     407       26142 :     pq_startmsgread();
     408       26142 :     firstbyte = pq_peekbyte();
     409       26142 :     pq_endmsgread();
     410       26142 :     if (firstbyte == EOF)
     411             :     {
     412             :         /*
     413             :          * Like in ProcessStartupPacket, if we get no data at all, don't
     414             :          * clutter the log with a complaint.
     415             :          */
     416           4 :         return STATUS_ERROR;
     417             :     }
     418             : 
     419       26138 :     if (firstbyte != 0x16)
     420             :     {
     421             :         /* Not an SSL handshake message */
     422       26128 :         return STATUS_OK;
     423             :     }
     424             : 
     425             :     /*
     426             :      * First byte indicates standard SSL handshake message
     427             :      *
     428             :      * (It can't be a Postgres startup length because in network byte order
     429             :      * that would be a startup packet hundreds of megabytes long)
     430             :      */
     431             : 
     432             : #ifdef USE_SSL
     433          10 :     if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX)
     434             :     {
     435             :         /* SSL not supported */
     436           4 :         goto reject;
     437             :     }
     438             : 
     439           6 :     if (secure_open_server(port) == -1)
     440             :     {
     441             :         /*
     442             :          * we assume secure_open_server() sent an appropriate TLS alert
     443             :          * already
     444             :          */
     445           0 :         goto reject;
     446             :     }
     447             :     Assert(port->ssl_in_use);
     448             : 
     449           6 :     if (!port->alpn_used)
     450             :     {
     451           0 :         ereport(COMMERROR,
     452             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     453             :                  errmsg("received direct SSL connection request without ALPN protocol negotiation extension")));
     454           0 :         goto reject;
     455             :     }
     456             : 
     457           6 :     if (Trace_connection_negotiation)
     458           6 :         ereport(LOG,
     459             :                 (errmsg("direct SSL connection accepted")));
     460           6 :     return STATUS_OK;
     461             : #else
     462             :     /* SSL not supported by this build */
     463             :     goto reject;
     464             : #endif
     465             : 
     466           4 : reject:
     467           4 :     if (Trace_connection_negotiation)
     468           4 :         ereport(LOG,
     469             :                 (errmsg("direct SSL connection rejected")));
     470           4 :     return STATUS_ERROR;
     471             : }
     472             : 
     473             : /*
     474             :  * Read a client's startup packet and do something according to it.
     475             :  *
     476             :  * Returns STATUS_OK or STATUS_ERROR, or might call ereport(FATAL) and
     477             :  * not return at all.
     478             :  *
     479             :  * (Note that ereport(FATAL) stuff is sent to the client, so only use it
     480             :  * if that's what you want.  Return STATUS_ERROR if you don't want to
     481             :  * send anything to the client, which would typically be appropriate
     482             :  * if we detect a communications failure.)
     483             :  *
     484             :  * Set ssl_done and/or gss_done when negotiation of an encrypted layer
     485             :  * (currently, TLS or GSSAPI) is completed. A successful negotiation of either
     486             :  * encryption layer sets both flags, but a rejected negotiation sets only the
     487             :  * flag for that layer, since the client may wish to try the other one. We
     488             :  * should make no assumption here about the order in which the client may make
     489             :  * requests.
     490             :  */
     491             : static int
     492       26638 : ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
     493             : {
     494             :     int32       len;
     495       26638 :     char       *buf = NULL;
     496             :     ProtocolVersion proto;
     497             :     MemoryContext oldcontext;
     498             : 
     499       26638 :     pq_startmsgread();
     500             : 
     501             :     /*
     502             :      * Grab the first byte of the length word separately, so that we can tell
     503             :      * whether we have no data at all or an incomplete packet.  (This might
     504             :      * sound inefficient, but it's not really, because of buffering in
     505             :      * pqcomm.c.)
     506             :      */
     507       26638 :     if (pq_getbytes(&len, 1) == EOF)
     508             :     {
     509             :         /*
     510             :          * If we get no data at all, don't clutter the log with a complaint;
     511             :          * such cases often occur for legitimate reasons.  An example is that
     512             :          * we might be here after responding to NEGOTIATE_SSL_CODE, and if the
     513             :          * client didn't like our response, it'll probably just drop the
     514             :          * connection.  Service-monitoring software also often just opens and
     515             :          * closes a connection without sending anything.  (So do port
     516             :          * scanners, which may be less benign, but it's not really our job to
     517             :          * notice those.)
     518             :          */
     519          30 :         goto fail;
     520             :     }
     521             : 
     522       26608 :     if (pq_getbytes(((char *) &len) + 1, 3) == EOF)
     523             :     {
     524             :         /* Got a partial length word, so bleat about that */
     525           0 :         if (!ssl_done && !gss_done)
     526           0 :             ereport(COMMERROR,
     527             :                     (errcode(ERRCODE_PROTOCOL_VIOLATION),
     528             :                      errmsg("incomplete startup packet")));
     529           0 :         goto fail;
     530             :     }
     531             : 
     532       26608 :     len = pg_ntoh32(len);
     533       26608 :     len -= 4;
     534             : 
     535       26608 :     if (len < (int32) sizeof(ProtocolVersion) ||
     536       26608 :         len > MAX_STARTUP_PACKET_LENGTH)
     537             :     {
     538           0 :         ereport(COMMERROR,
     539             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     540             :                  errmsg("invalid length of startup packet")));
     541           0 :         goto fail;
     542             :     }
     543             : 
     544             :     /*
     545             :      * Allocate space to hold the startup packet, plus one extra byte that's
     546             :      * initialized to be zero.  This ensures we will have null termination of
     547             :      * all strings inside the packet.
     548             :      */
     549       26608 :     buf = palloc(len + 1);
     550       26608 :     buf[len] = '\0';
     551             : 
     552       26608 :     if (pq_getbytes(buf, len) == EOF)
     553             :     {
     554           0 :         ereport(COMMERROR,
     555             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     556             :                  errmsg("incomplete startup packet")));
     557           0 :         goto fail;
     558             :     }
     559       26608 :     pq_endmsgread();
     560             : 
     561             :     /*
     562             :      * The first field is either a protocol version number or a special
     563             :      * request code.
     564             :      */
     565       26608 :     port->proto = proto = pg_ntoh32(*((ProtocolVersion *) buf));
     566             : 
     567       26608 :     if (proto == CANCEL_REQUEST_CODE)
     568             :     {
     569          32 :         ProcessCancelRequestPacket(port, buf, len);
     570             :         /* Not really an error, but we don't want to proceed further */
     571          32 :         goto fail;
     572             :     }
     573             : 
     574       26576 :     if (proto == NEGOTIATE_SSL_CODE && !ssl_done)
     575             :     {
     576             :         char        SSLok;
     577             : 
     578             : #ifdef USE_SSL
     579             : 
     580             :         /*
     581             :          * No SSL when disabled or on Unix sockets.
     582             :          *
     583             :          * Also no SSL negotiation if we already have a direct SSL connection
     584             :          */
     585         544 :         if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX || port->ssl_in_use)
     586         296 :             SSLok = 'N';
     587             :         else
     588         248 :             SSLok = 'S';        /* Support for SSL */
     589             : #else
     590             :         SSLok = 'N';            /* No support for SSL */
     591             : #endif
     592             : 
     593         544 :         if (Trace_connection_negotiation)
     594             :         {
     595          24 :             if (SSLok == 'S')
     596          16 :                 ereport(LOG,
     597             :                         (errmsg("SSLRequest accepted")));
     598             :             else
     599           8 :                 ereport(LOG,
     600             :                         (errmsg("SSLRequest rejected")));
     601             :         }
     602             : 
     603         544 :         while (secure_write(port, &SSLok, 1) != 1)
     604             :         {
     605           0 :             if (errno == EINTR)
     606           0 :                 continue;       /* if interrupted, just retry */
     607           0 :             ereport(COMMERROR,
     608             :                     (errcode_for_socket_access(),
     609             :                      errmsg("failed to send SSL negotiation response: %m")));
     610          38 :             goto fail;          /* close the connection */
     611             :         }
     612             : 
     613             : #ifdef USE_SSL
     614         544 :         if (SSLok == 'S' && secure_open_server(port) == -1)
     615          38 :             goto fail;
     616             : #endif
     617             : 
     618         504 :         pfree(buf);
     619             : 
     620             :         /*
     621             :          * At this point we should have no data already buffered.  If we do,
     622             :          * it was received before we performed the SSL handshake, so it wasn't
     623             :          * encrypted and indeed may have been injected by a man-in-the-middle.
     624             :          * We report this case to the client.
     625             :          */
     626         504 :         if (pq_buffer_remaining_data() > 0)
     627           0 :             ereport(FATAL,
     628             :                     (errcode(ERRCODE_PROTOCOL_VIOLATION),
     629             :                      errmsg("received unencrypted data after SSL request"),
     630             :                      errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack.")));
     631             : 
     632             :         /*
     633             :          * regular startup packet, cancel, etc packet should follow, but not
     634             :          * another SSL negotiation request, and a GSS request should only
     635             :          * follow if SSL was rejected (client may negotiate in either order)
     636             :          */
     637         504 :         return ProcessStartupPacket(port, true, SSLok == 'S');
     638             :     }
     639       26032 :     else if (proto == NEGOTIATE_GSS_CODE && !gss_done)
     640             :     {
     641           0 :         char        GSSok = 'N';
     642             : 
     643             : #ifdef ENABLE_GSS
     644             :         /* No GSSAPI encryption when on Unix socket */
     645             :         if (port->laddr.addr.ss_family != AF_UNIX)
     646             :             GSSok = 'G';
     647             : #endif
     648             : 
     649           0 :         if (Trace_connection_negotiation)
     650             :         {
     651           0 :             if (GSSok == 'G')
     652           0 :                 ereport(LOG,
     653             :                         (errmsg("GSSENCRequest accepted")));
     654             :             else
     655           0 :                 ereport(LOG,
     656             :                         (errmsg("GSSENCRequest rejected")));
     657             :         }
     658             : 
     659           0 :         while (secure_write(port, &GSSok, 1) != 1)
     660             :         {
     661           0 :             if (errno == EINTR)
     662           0 :                 continue;
     663           0 :             ereport(COMMERROR,
     664             :                     (errcode_for_socket_access(),
     665             :                      errmsg("failed to send GSSAPI negotiation response: %m")));
     666           0 :             goto fail;          /* close the connection */
     667             :         }
     668             : 
     669             : #ifdef ENABLE_GSS
     670             :         if (GSSok == 'G' && secure_open_gssapi(port) == -1)
     671             :             goto fail;
     672             : #endif
     673             : 
     674           0 :         pfree(buf);
     675             : 
     676             :         /*
     677             :          * At this point we should have no data already buffered.  If we do,
     678             :          * it was received before we performed the GSS handshake, so it wasn't
     679             :          * encrypted and indeed may have been injected by a man-in-the-middle.
     680             :          * We report this case to the client.
     681             :          */
     682           0 :         if (pq_buffer_remaining_data() > 0)
     683           0 :             ereport(FATAL,
     684             :                     (errcode(ERRCODE_PROTOCOL_VIOLATION),
     685             :                      errmsg("received unencrypted data after GSSAPI encryption request"),
     686             :                      errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack.")));
     687             : 
     688             :         /*
     689             :          * regular startup packet, cancel, etc packet should follow, but not
     690             :          * another GSS negotiation request, and an SSL request should only
     691             :          * follow if GSS was rejected (client may negotiate in either order)
     692             :          */
     693           0 :         return ProcessStartupPacket(port, GSSok == 'G', true);
     694             :     }
     695             : 
     696             :     /* Could add additional special packet types here */
     697             : 
     698             :     /*
     699             :      * Set FrontendProtocol now so that ereport() knows what format to send if
     700             :      * we fail during startup. We use the protocol version requested by the
     701             :      * client unless it's higher than the latest version we support. It's
     702             :      * possible that error message fields might look different in newer
     703             :      * protocol versions, but that's something those new clients should be
     704             :      * able to deal with.
     705             :      */
     706       26032 :     FrontendProtocol = Min(proto, PG_PROTOCOL_LATEST);
     707             : 
     708             :     /* Check that the major protocol version is in range. */
     709       26032 :     if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
     710       26032 :         PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST))
     711           0 :         ereport(FATAL,
     712             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     713             :                  errmsg("unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u",
     714             :                         PG_PROTOCOL_MAJOR(proto), PG_PROTOCOL_MINOR(proto),
     715             :                         PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST),
     716             :                         PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST),
     717             :                         PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))));
     718             : 
     719             :     /*
     720             :      * Now fetch parameters out of startup packet and save them into the Port
     721             :      * structure.
     722             :      */
     723       26032 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     724             : 
     725             :     /* Handle protocol version 3 startup packet */
     726             :     {
     727       26032 :         int32       offset = sizeof(ProtocolVersion);
     728       26032 :         List       *unrecognized_protocol_options = NIL;
     729             : 
     730             :         /*
     731             :          * Scan packet body for name/option pairs.  We can assume any string
     732             :          * beginning within the packet body is null-terminated, thanks to
     733             :          * zeroing extra byte above.
     734             :          */
     735       26032 :         port->guc_options = NIL;
     736             : 
     737      125512 :         while (offset < len)
     738             :         {
     739      125512 :             char       *nameptr = buf + offset;
     740             :             int32       valoffset;
     741             :             char       *valptr;
     742             : 
     743      125512 :             if (*nameptr == '\0')
     744       26032 :                 break;          /* found packet terminator */
     745       99480 :             valoffset = offset + strlen(nameptr) + 1;
     746       99480 :             if (valoffset >= len)
     747           0 :                 break;          /* missing value, will complain below */
     748       99480 :             valptr = buf + valoffset;
     749             : 
     750       99480 :             if (strcmp(nameptr, "database") == 0)
     751       26032 :                 port->database_name = pstrdup(valptr);
     752       73448 :             else if (strcmp(nameptr, "user") == 0)
     753       26032 :                 port->user_name = pstrdup(valptr);
     754       47416 :             else if (strcmp(nameptr, "options") == 0)
     755        7790 :                 port->cmdline_options = pstrdup(valptr);
     756       39626 :             else if (strcmp(nameptr, "replication") == 0)
     757             :             {
     758             :                 /*
     759             :                  * Due to backward compatibility concerns the replication
     760             :                  * parameter is a hybrid beast which allows the value to be
     761             :                  * either boolean or the string 'database'. The latter
     762             :                  * connects to a specific database which is e.g. required for
     763             :                  * logical decoding while.
     764             :                  */
     765        2368 :                 if (strcmp(valptr, "database") == 0)
     766             :                 {
     767        1380 :                     am_walsender = true;
     768        1380 :                     am_db_walsender = true;
     769             :                 }
     770         988 :                 else if (!parse_bool(valptr, &am_walsender))
     771           0 :                     ereport(FATAL,
     772             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     773             :                              errmsg("invalid value for parameter \"%s\": \"%s\"",
     774             :                                     "replication",
     775             :                                     valptr),
     776             :                              errhint("Valid values are: \"false\", 0, \"true\", 1, \"database\".")));
     777             :             }
     778       37258 :             else if (strncmp(nameptr, "_pq_.", 5) == 0)
     779             :             {
     780             :                 /*
     781             :                  * Any option beginning with _pq_. is reserved for use as a
     782             :                  * protocol-level option, but at present no such options are
     783             :                  * defined.
     784             :                  */
     785             :                 unrecognized_protocol_options =
     786           0 :                     lappend(unrecognized_protocol_options, pstrdup(nameptr));
     787             :             }
     788             :             else
     789             :             {
     790             :                 /* Assume it's a generic GUC option */
     791       37258 :                 port->guc_options = lappend(port->guc_options,
     792       37258 :                                             pstrdup(nameptr));
     793       37258 :                 port->guc_options = lappend(port->guc_options,
     794       37258 :                                             pstrdup(valptr));
     795             : 
     796             :                 /*
     797             :                  * Copy application_name to port if we come across it.  This
     798             :                  * is done so we can log the application_name in the
     799             :                  * connection authorization message.  Note that the GUC would
     800             :                  * be used but we haven't gone through GUC setup yet.
     801             :                  */
     802       37258 :                 if (strcmp(nameptr, "application_name") == 0)
     803             :                 {
     804       26022 :                     port->application_name = pg_clean_ascii(valptr, 0);
     805             :                 }
     806             :             }
     807       99480 :             offset = valoffset + strlen(valptr) + 1;
     808             :         }
     809             : 
     810             :         /*
     811             :          * If we didn't find a packet terminator exactly at the end of the
     812             :          * given packet length, complain.
     813             :          */
     814       26032 :         if (offset != len - 1)
     815           0 :             ereport(FATAL,
     816             :                     (errcode(ERRCODE_PROTOCOL_VIOLATION),
     817             :                      errmsg("invalid startup packet layout: expected terminator as last byte")));
     818             : 
     819             :         /*
     820             :          * If the client requested a newer protocol version or if the client
     821             :          * requested any protocol options we didn't recognize, let them know
     822             :          * the newest minor protocol version we do support and the names of
     823             :          * any unrecognized options.
     824             :          */
     825       26032 :         if (PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST) ||
     826             :             unrecognized_protocol_options != NIL)
     827           0 :             SendNegotiateProtocolVersion(unrecognized_protocol_options);
     828             :     }
     829             : 
     830             :     /* Check a user name was given. */
     831       26032 :     if (port->user_name == NULL || port->user_name[0] == '\0')
     832           0 :         ereport(FATAL,
     833             :                 (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
     834             :                  errmsg("no PostgreSQL user name specified in startup packet")));
     835             : 
     836             :     /* The database defaults to the user name. */
     837       26032 :     if (port->database_name == NULL || port->database_name[0] == '\0')
     838           0 :         port->database_name = pstrdup(port->user_name);
     839             : 
     840             :     /*
     841             :      * Truncate given database and user names to length of a Postgres name.
     842             :      * This avoids lookup failures when overlength names are given.
     843             :      */
     844       26032 :     if (strlen(port->database_name) >= NAMEDATALEN)
     845           0 :         port->database_name[NAMEDATALEN - 1] = '\0';
     846       26032 :     if (strlen(port->user_name) >= NAMEDATALEN)
     847           0 :         port->user_name[NAMEDATALEN - 1] = '\0';
     848             : 
     849       26032 :     if (am_walsender)
     850        2368 :         MyBackendType = B_WAL_SENDER;
     851             :     else
     852       23664 :         MyBackendType = B_BACKEND;
     853             : 
     854             :     /*
     855             :      * Normal walsender backends, e.g. for streaming replication, are not
     856             :      * connected to a particular database. But walsenders used for logical
     857             :      * replication need to connect to a specific database. We allow streaming
     858             :      * replication commands to be issued even if connected to a database as it
     859             :      * can make sense to first make a basebackup and then stream changes
     860             :      * starting from that.
     861             :      */
     862       26032 :     if (am_walsender && !am_db_walsender)
     863         988 :         port->database_name[0] = '\0';
     864             : 
     865             :     /*
     866             :      * Done filling the Port structure
     867             :      */
     868       26032 :     MemoryContextSwitchTo(oldcontext);
     869             : 
     870       26032 :     pfree(buf);
     871             : 
     872       26032 :     return STATUS_OK;
     873             : 
     874         100 : fail:
     875             :     /* be tidy, just to avoid Valgrind complaints */
     876         100 :     if (buf)
     877          70 :         pfree(buf);
     878             : 
     879         100 :     return STATUS_ERROR;
     880             : }
     881             : 
     882             : /*
     883             :  * The client has sent a cancel request packet, not a normal
     884             :  * start-a-new-connection packet.  Perform the necessary processing.  Nothing
     885             :  * is sent back to the client.
     886             :  */
     887             : static void
     888          32 : ProcessCancelRequestPacket(Port *port, void *pkt, int pktlen)
     889             : {
     890             :     CancelRequestPacket *canc;
     891             :     int         len;
     892             : 
     893          32 :     if (pktlen < offsetof(CancelRequestPacket, cancelAuthCode))
     894             :     {
     895           0 :         ereport(COMMERROR,
     896             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     897             :                  errmsg("invalid length of cancel request packet")));
     898           0 :         return;
     899             :     }
     900          32 :     len = pktlen - offsetof(CancelRequestPacket, cancelAuthCode);
     901          32 :     if (len == 0 || len > 256)
     902             :     {
     903           0 :         ereport(COMMERROR,
     904             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     905             :                  errmsg("invalid length of cancel key in cancel request packet")));
     906           0 :         return;
     907             :     }
     908             : 
     909          32 :     canc = (CancelRequestPacket *) pkt;
     910          32 :     SendCancelRequest(pg_ntoh32(canc->backendPID), canc->cancelAuthCode, len);
     911             : }
     912             : 
     913             : /*
     914             :  * Send a NegotiateProtocolVersion to the client.  This lets the client know
     915             :  * that they have either requested a newer minor protocol version than we are
     916             :  * able to speak, or at least one protocol option that we don't understand, or
     917             :  * possibly both. FrontendProtocol has already been set to the version
     918             :  * requested by the client or the highest version we know how to speak,
     919             :  * whichever is older. If the highest version that we know how to speak is too
     920             :  * old for the client, it can abandon the connection.
     921             :  *
     922             :  * We also include in the response a list of protocol options we didn't
     923             :  * understand.  This allows clients to include optional parameters that might
     924             :  * be present either in newer protocol versions or third-party protocol
     925             :  * extensions without fear of having to reconnect if those options are not
     926             :  * understood, while at the same time making certain that the client is aware
     927             :  * of which options were actually accepted.
     928             :  */
     929             : static void
     930           0 : SendNegotiateProtocolVersion(List *unrecognized_protocol_options)
     931             : {
     932             :     StringInfoData buf;
     933             :     ListCell   *lc;
     934             : 
     935           0 :     pq_beginmessage(&buf, PqMsg_NegotiateProtocolVersion);
     936           0 :     pq_sendint32(&buf, FrontendProtocol);
     937           0 :     pq_sendint32(&buf, list_length(unrecognized_protocol_options));
     938           0 :     foreach(lc, unrecognized_protocol_options)
     939           0 :         pq_sendstring(&buf, lfirst(lc));
     940           0 :     pq_endmessage(&buf);
     941             : 
     942             :     /* no need to flush, some other message will follow */
     943           0 : }
     944             : 
     945             : 
     946             : /*
     947             :  * SIGTERM while processing startup packet.
     948             :  *
     949             :  * Running proc_exit() from a signal handler would be quite unsafe.
     950             :  * However, since we have not yet touched shared memory, we can just
     951             :  * pull the plug and exit without running any atexit handlers.
     952             :  *
     953             :  * One might be tempted to try to send a message, or log one, indicating
     954             :  * why we are disconnecting.  However, that would be quite unsafe in itself.
     955             :  * Also, it seems undesirable to provide clues about the database's state
     956             :  * to a client that has not yet completed authentication, or even sent us
     957             :  * a startup packet.
     958             :  */
     959             : static void
     960           0 : process_startup_packet_die(SIGNAL_ARGS)
     961             : {
     962           0 :     _exit(1);
     963             : }
     964             : 
     965             : /*
     966             :  * Timeout while processing startup packet.
     967             :  * As for process_startup_packet_die(), we exit via _exit(1).
     968             :  */
     969             : static void
     970           0 : StartupPacketTimeoutHandler(void)
     971             : {
     972           0 :     _exit(1);
     973             : }
     974             : 
     975             : /*
     976             :  * Helper for the log_connections GUC check hook.
     977             :  *
     978             :  * `elemlist` is a listified version of the string input passed to the
     979             :  * log_connections GUC check hook, check_log_connections().
     980             :  * check_log_connections() is responsible for cleaning up `elemlist`.
     981             :  *
     982             :  * validate_log_connections_options() returns false if an error was
     983             :  * encountered and the GUC input could not be validated and true otherwise.
     984             :  *
     985             :  * `flags` returns the flags that should be stored in the log_connections GUC
     986             :  * by its assign hook.
     987             :  */
     988             : static bool
     989        2444 : validate_log_connections_options(List *elemlist, uint32 *flags)
     990             : {
     991             :     ListCell   *l;
     992             :     char       *item;
     993             : 
     994             :     /*
     995             :      * For backwards compatibility, we accept these tokens by themselves.
     996             :      *
     997             :      * Prior to PostgreSQL 18, log_connections was a boolean GUC that accepted
     998             :      * any unambiguous substring of 'true', 'false', 'yes', 'no', 'on', and
     999             :      * 'off'. Since log_connections became a list of strings in 18, we only
    1000             :      * accept complete option strings.
    1001             :      */
    1002             :     static const struct config_enum_entry compat_options[] = {
    1003             :         {"off", 0},
    1004             :         {"false", 0},
    1005             :         {"no", 0},
    1006             :         {"0", 0},
    1007             :         {"on", LOG_CONNECTION_ON},
    1008             :         {"true", LOG_CONNECTION_ON},
    1009             :         {"yes", LOG_CONNECTION_ON},
    1010             :         {"1", LOG_CONNECTION_ON},
    1011             :     };
    1012             : 
    1013        2444 :     *flags = 0;
    1014             : 
    1015             :     /* If an empty string was passed, we're done */
    1016        2444 :     if (list_length(elemlist) == 0)
    1017        2212 :         return true;
    1018             : 
    1019             :     /*
    1020             :      * Now check for the backwards compatibility options. They must always be
    1021             :      * specified on their own, so we error out if the first option is a
    1022             :      * backwards compatibility option and other options are also specified.
    1023             :      */
    1024         232 :     item = linitial(elemlist);
    1025             : 
    1026        2080 :     for (size_t i = 0; i < lengthof(compat_options); i++)
    1027             :     {
    1028        1850 :         struct config_enum_entry option = compat_options[i];
    1029             : 
    1030        1850 :         if (pg_strcasecmp(item, option.name) != 0)
    1031        1848 :             continue;
    1032             : 
    1033           2 :         if (list_length(elemlist) > 1)
    1034             :         {
    1035           0 :             GUC_check_errdetail("Cannot specify log_connections option \"%s\" in a list with other options.",
    1036             :                                 item);
    1037           2 :             return false;
    1038             :         }
    1039             : 
    1040           2 :         *flags = option.val;
    1041           2 :         return true;
    1042             :     }
    1043             : 
    1044             :     /* Now check the aspect options. The empty string was already handled */
    1045         526 :     foreach(l, elemlist)
    1046             :     {
    1047             :         static const struct config_enum_entry options[] = {
    1048             :             {"receipt", LOG_CONNECTION_RECEIPT},
    1049             :             {"authentication", LOG_CONNECTION_AUTHENTICATION},
    1050             :             {"authorization", LOG_CONNECTION_AUTHORIZATION},
    1051             :             {"setup_durations", LOG_CONNECTION_SETUP_DURATIONS},
    1052             :             {"all", LOG_CONNECTION_ALL},
    1053             :         };
    1054             : 
    1055         296 :         item = lfirst(l);
    1056         986 :         for (size_t i = 0; i < lengthof(options); i++)
    1057             :         {
    1058         986 :             struct config_enum_entry option = options[i];
    1059             : 
    1060         986 :             if (pg_strcasecmp(item, option.name) == 0)
    1061             :             {
    1062         296 :                 *flags |= option.val;
    1063         296 :                 goto next;
    1064             :             }
    1065             :         }
    1066             : 
    1067           0 :         GUC_check_errdetail("Invalid option \"%s\".", item);
    1068           0 :         return false;
    1069             : 
    1070         296 : next:   ;
    1071             :     }
    1072             : 
    1073         230 :     return true;
    1074             : }
    1075             : 
    1076             : 
    1077             : /*
    1078             :  * GUC check hook for log_connections
    1079             :  */
    1080             : bool
    1081        2444 : check_log_connections(char **newval, void **extra, GucSource source)
    1082             : {
    1083             :     uint32      flags;
    1084             :     char       *rawstring;
    1085             :     List       *elemlist;
    1086             :     bool        success;
    1087             : 
    1088             :     /* Need a modifiable copy of string */
    1089        2444 :     rawstring = pstrdup(*newval);
    1090             : 
    1091        2444 :     if (!SplitIdentifierString(rawstring, ',', &elemlist))
    1092             :     {
    1093           0 :         GUC_check_errdetail("Invalid list syntax in parameter \"%s\".", "log_connections");
    1094           0 :         pfree(rawstring);
    1095           0 :         list_free(elemlist);
    1096           0 :         return false;
    1097             :     }
    1098             : 
    1099             :     /* Validation logic is all in the helper */
    1100        2444 :     success = validate_log_connections_options(elemlist, &flags);
    1101             : 
    1102             :     /* Time for cleanup */
    1103        2444 :     pfree(rawstring);
    1104        2444 :     list_free(elemlist);
    1105             : 
    1106        2444 :     if (!success)
    1107           0 :         return false;
    1108             : 
    1109             :     /*
    1110             :      * We succeeded, so allocate `extra` and save the flags there for use by
    1111             :      * assign_log_connections().
    1112             :      */
    1113        2444 :     *extra = guc_malloc(LOG, sizeof(int));
    1114        2444 :     if (!*extra)
    1115           0 :         return false;
    1116        2444 :     *((int *) *extra) = flags;
    1117             : 
    1118        2444 :     return true;
    1119             : }
    1120             : 
    1121             : /*
    1122             :  * GUC assign hook for log_connections
    1123             :  */
    1124             : void
    1125        2436 : assign_log_connections(const char *newval, void *extra)
    1126             : {
    1127        2436 :     log_connections = *((int *) extra);
    1128        2436 : }

Generated by: LCOV version 1.16