LCOV - code coverage report
Current view: top level - src/backend/tcop - backend_startup.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 76.0 % 288 219
Test Date: 2026-02-17 17:20:33 Functions: 72.7 % 11 8
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-2026, 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        14074 : BackendMain(const void *startup_data, size_t startup_data_len)
      77              : {
      78        14074 :     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        14074 :     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        13696 :     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        13693 :     MemoryContextSwitchTo(TopMemoryContext);
     123              : 
     124        13693 :     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        14074 : 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        14074 :     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        14074 :     if (PreAuthDelay > 0)
     162            0 :         pg_usleep(PreAuthDelay * 1000000L);
     163              : 
     164              :     /* This flag will remain set until InitPostgres finishes authentication */
     165        14074 :     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        14074 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     177        14074 :     port = MyProcPort = pq_init(client_sock);
     178        14074 :     MemoryContextSwitchTo(oldcontext);
     179              : 
     180        14074 :     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        14074 :     port->remote_host = "";
     184        14074 :     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        14074 :     pqsignal(SIGTERM, process_startup_packet_die);
     197              :     /* SIGQUIT handler was already set up by InitPostmasterChild */
     198        14074 :     InitializeTimeouts();       /* establishes SIGALRM handler */
     199        14074 :     sigprocmask(SIG_SETMASK, &StartupBlockSig, NULL);
     200              : 
     201              :     /*
     202              :      * Get the remote host name and port for logging and status display.
     203              :      */
     204        14074 :     remote_host[0] = '\0';
     205        14074 :     remote_port[0] = '\0';
     206        14074 :     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        14074 :     port->remote_host = MemoryContextStrdup(TopMemoryContext, remote_host);
     219        14074 :     port->remote_port = MemoryContextStrdup(TopMemoryContext, remote_port);
     220              : 
     221              :     /* And now we can log that the connection was received, if enabled */
     222        14074 :     if (log_connections & LOG_CONNECTION_RECEIPT)
     223              :     {
     224          395 :         if (remote_port[0])
     225          149 :             ereport(LOG,
     226              :                     (errmsg("connection received: host=%s port=%s",
     227              :                             remote_host,
     228              :                             remote_port)));
     229              :         else
     230          246 :             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        14074 :     INJECTION_POINT("backend-initialize", NULL);
     238        14074 :     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        14074 :     if (log_hostname &&
     262          120 :         ret == 0 &&
     263          120 :         strspn(remote_host, "0123456789.") < strlen(remote_host) &&
     264          120 :         strspn(remote_host, "0123456789ABCDEFabcdef:") < strlen(remote_host))
     265              :     {
     266          120 :         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        14074 :     RegisterTimeout(STARTUP_PACKET_TIMEOUT, StartupPacketTimeoutHandler);
     285        14074 :     enable_timeout_after(STARTUP_PACKET_TIMEOUT, AuthenticationTimeout * 1000);
     286              : 
     287              :     /* Handle direct SSL handshake */
     288        14074 :     status = ProcessSSLStartup(port);
     289              : 
     290              :     /*
     291              :      * Receive the startup packet (which might turn out to be a cancel request
     292              :      * packet).
     293              :      */
     294        14074 :     if (status == STATUS_OK)
     295        14071 :         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        14073 :     if (status == STATUS_OK)
     303              :     {
     304        14021 :         switch (cac)
     305              :         {
     306          267 :             case CAC_STARTUP:
     307          267 :                 ereport(FATAL,
     308              :                         (errcode(ERRCODE_CANNOT_CONNECT_NOW),
     309              :                          errmsg("the database system is starting up")));
     310              :                 break;
     311            8 :             case CAC_NOTHOTSTANDBY:
     312            8 :                 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            8 :                 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            8 :                     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           45 :             case CAC_SHUTDOWN:
     331           45 :                 ereport(FATAL,
     332              :                         (errcode(ERRCODE_CANNOT_CONNECT_NOW),
     333              :                          errmsg("the database system is shutting down")));
     334              :                 break;
     335            4 :             case CAC_RECOVERY:
     336            4 :                 ereport(FATAL,
     337              :                         (errcode(ERRCODE_CANNOT_CONNECT_NOW),
     338              :                          errmsg("the database system is in recovery mode")));
     339              :                 break;
     340            1 :             case CAC_TOOMANY:
     341            1 :                 ereport(FATAL,
     342              :                         (errcode(ERRCODE_TOO_MANY_CONNECTIONS),
     343              :                          errmsg("sorry, too many clients already")));
     344              :                 break;
     345        13696 :             case CAC_OK:
     346        13696 :                 break;
     347              :         }
     348              :     }
     349              : 
     350              :     /*
     351              :      * Disable the timeout, and prevent SIGTERM again.
     352              :      */
     353        13748 :     disable_timeout(STARTUP_PACKET_TIMEOUT, false);
     354        13748 :     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        13748 :     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        13748 :     if (status != STATUS_OK)
     372           52 :         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        13696 :     initStringInfo(&ps_data);
     379        13696 :     if (am_walsender)
     380         1207 :         appendStringInfo(&ps_data, "%s ", GetBackendTypeDesc(B_WAL_SENDER));
     381        13696 :     appendStringInfo(&ps_data, "%s ", port->user_name);
     382        13696 :     if (port->database_name[0] != '\0')
     383        13219 :         appendStringInfo(&ps_data, "%s ", port->database_name);
     384        13696 :     appendStringInfoString(&ps_data, port->remote_host);
     385        13696 :     if (port->remote_port[0] != '\0')
     386          255 :         appendStringInfo(&ps_data, "(%s)", port->remote_port);
     387              : 
     388        13696 :     init_ps_display(ps_data.data);
     389        13696 :     pfree(ps_data.data);
     390              : 
     391        13696 :     set_ps_display("initializing");
     392        13696 : }
     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        14074 : ProcessSSLStartup(Port *port)
     402              : {
     403              :     int         firstbyte;
     404              : 
     405              :     Assert(!port->ssl_in_use);
     406              : 
     407        14074 :     pq_startmsgread();
     408        14074 :     firstbyte = pq_peekbyte();
     409        14074 :     pq_endmsgread();
     410        14074 :     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            1 :         return STATUS_ERROR;
     417              :     }
     418              : 
     419        14073 :     if (firstbyte != 0x16)
     420              :     {
     421              :         /* Not an SSL handshake message */
     422        14068 :         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            5 :     if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX)
     434              :     {
     435              :         /* SSL not supported */
     436            2 :         goto reject;
     437              :     }
     438              : 
     439            3 :     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            3 :     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            3 :     if (Trace_connection_negotiation)
     458            3 :         ereport(LOG,
     459              :                 (errmsg("direct SSL connection accepted")));
     460            3 :     return STATUS_OK;
     461              : #else
     462              :     /* SSL not supported by this build */
     463              :     goto reject;
     464              : #endif
     465              : 
     466            2 : reject:
     467            2 :     if (Trace_connection_negotiation)
     468            2 :         ereport(LOG,
     469              :                 (errmsg("direct SSL connection rejected")));
     470            2 :     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        14326 : ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
     493              : {
     494              :     int32       len;
     495        14326 :     char       *buf = NULL;
     496              :     ProtocolVersion proto;
     497              :     MemoryContext oldcontext;
     498              : 
     499        14326 :     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        14326 :     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           15 :         goto fail;
     520              :     }
     521              : 
     522        14311 :     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        14311 :     len = pg_ntoh32(len);
     533        14311 :     len -= 4;
     534              : 
     535        14311 :     if (len < (int32) sizeof(ProtocolVersion) ||
     536        14311 :         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        14311 :     buf = palloc(len + 1);
     550        14311 :     buf[len] = '\0';
     551              : 
     552        14311 :     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        14311 :     pq_endmsgread();
     560              : 
     561              :     /*
     562              :      * The first field is either a protocol version number or a special
     563              :      * request code.
     564              :      */
     565        14311 :     port->proto = proto = pg_ntoh32(*((ProtocolVersion *) buf));
     566              : 
     567        14311 :     if (proto == CANCEL_REQUEST_CODE)
     568              :     {
     569           15 :         ProcessCancelRequestPacket(port, buf, len);
     570              :         /* Not really an error, but we don't want to proceed further */
     571           15 :         goto fail;
     572              :     }
     573              : 
     574        14296 :     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          275 :         if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX || port->ssl_in_use)
     586          148 :             SSLok = 'N';
     587              :         else
     588          127 :             SSLok = 'S';        /* Support for SSL */
     589              : #else
     590              :         SSLok = 'N';            /* No support for SSL */
     591              : #endif
     592              : 
     593          275 :         if (Trace_connection_negotiation)
     594              :         {
     595           12 :             if (SSLok == 'S')
     596            8 :                 ereport(LOG,
     597              :                         (errmsg("SSLRequest accepted")));
     598              :             else
     599            4 :                 ereport(LOG,
     600              :                         (errmsg("SSLRequest rejected")));
     601              :         }
     602              : 
     603          275 :         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           19 :             goto fail;          /* close the connection */
     611              :         }
     612              : 
     613              : #ifdef USE_SSL
     614          275 :         if (SSLok == 'S' && secure_open_server(port) == -1)
     615           19 :             goto fail;
     616              : #endif
     617              : 
     618          255 :         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          255 :         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          255 :         return ProcessStartupPacket(port, true, SSLok == 'S');
     638              :     }
     639        14021 :     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        14021 :     FrontendProtocol = Min(proto, PG_PROTOCOL_LATEST);
     707              : 
     708              :     /* Check that the major protocol version is in range. */
     709        14021 :     if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
     710        14021 :         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        14021 :     oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     724              : 
     725              :     /* Handle protocol version 3 startup packet */
     726              :     {
     727        14021 :         int32       offset = sizeof(ProtocolVersion);
     728        14021 :         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        14021 :         port->guc_options = NIL;
     736              : 
     737        67447 :         while (offset < len)
     738              :         {
     739        67447 :             char       *nameptr = buf + offset;
     740              :             int32       valoffset;
     741              :             char       *valptr;
     742              : 
     743        67447 :             if (*nameptr == '\0')
     744        14021 :                 break;          /* found packet terminator */
     745        53426 :             valoffset = offset + strlen(nameptr) + 1;
     746        53426 :             if (valoffset >= len)
     747            0 :                 break;          /* missing value, will complain below */
     748        53426 :             valptr = buf + valoffset;
     749              : 
     750        53426 :             if (strcmp(nameptr, "database") == 0)
     751        14021 :                 port->database_name = pstrdup(valptr);
     752        39405 :             else if (strcmp(nameptr, "user") == 0)
     753        14021 :                 port->user_name = pstrdup(valptr);
     754        25384 :             else if (strcmp(nameptr, "options") == 0)
     755         4147 :                 port->cmdline_options = pstrdup(valptr);
     756        21237 :             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         1263 :                 if (strcmp(valptr, "database") == 0)
     766              :                 {
     767          742 :                     am_walsender = true;
     768          742 :                     am_db_walsender = true;
     769              :                 }
     770          521 :                 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        19974 :             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        19974 :                 port->guc_options = lappend(port->guc_options,
     792        19974 :                                             pstrdup(nameptr));
     793        19974 :                 port->guc_options = lappend(port->guc_options,
     794        19974 :                                             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        19974 :                 if (strcmp(nameptr, "application_name") == 0)
     803              :                 {
     804        14016 :                     port->application_name = pg_clean_ascii(valptr, 0);
     805              :                 }
     806              :             }
     807        53426 :             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        14021 :         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        14021 :         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        14021 :     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        14021 :     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        14021 :     if (strlen(port->database_name) >= NAMEDATALEN)
     845            0 :         port->database_name[NAMEDATALEN - 1] = '\0';
     846        14021 :     if (strlen(port->user_name) >= NAMEDATALEN)
     847            0 :         port->user_name[NAMEDATALEN - 1] = '\0';
     848              : 
     849              :     Assert(MyBackendType == B_BACKEND || MyBackendType == B_DEAD_END_BACKEND);
     850        14021 :     if (am_walsender)
     851         1263 :         MyBackendType = B_WAL_SENDER;
     852              : 
     853              :     /*
     854              :      * Normal walsender backends, e.g. for streaming replication, are not
     855              :      * connected to a particular database. But walsenders used for logical
     856              :      * replication need to connect to a specific database. We allow streaming
     857              :      * replication commands to be issued even if connected to a database as it
     858              :      * can make sense to first make a basebackup and then stream changes
     859              :      * starting from that.
     860              :      */
     861        14021 :     if (am_walsender && !am_db_walsender)
     862          521 :         port->database_name[0] = '\0';
     863              : 
     864              :     /*
     865              :      * Done filling the Port structure
     866              :      */
     867        14021 :     MemoryContextSwitchTo(oldcontext);
     868              : 
     869        14021 :     pfree(buf);
     870              : 
     871        14021 :     return STATUS_OK;
     872              : 
     873           49 : fail:
     874              :     /* be tidy, just to avoid Valgrind complaints */
     875           49 :     if (buf)
     876           34 :         pfree(buf);
     877              : 
     878           49 :     return STATUS_ERROR;
     879              : }
     880              : 
     881              : /*
     882              :  * The client has sent a cancel request packet, not a normal
     883              :  * start-a-new-connection packet.  Perform the necessary processing.  Nothing
     884              :  * is sent back to the client.
     885              :  */
     886              : static void
     887           15 : ProcessCancelRequestPacket(Port *port, void *pkt, int pktlen)
     888              : {
     889              :     CancelRequestPacket *canc;
     890              :     int         len;
     891              : 
     892           15 :     if (pktlen < offsetof(CancelRequestPacket, cancelAuthCode))
     893              :     {
     894            0 :         ereport(COMMERROR,
     895              :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     896              :                  errmsg("invalid length of cancel request packet")));
     897            0 :         return;
     898              :     }
     899           15 :     len = pktlen - offsetof(CancelRequestPacket, cancelAuthCode);
     900           15 :     if (len == 0 || len > 256)
     901              :     {
     902            0 :         ereport(COMMERROR,
     903              :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     904              :                  errmsg("invalid length of cancel key in cancel request packet")));
     905            0 :         return;
     906              :     }
     907              : 
     908           15 :     canc = (CancelRequestPacket *) pkt;
     909           15 :     SendCancelRequest(pg_ntoh32(canc->backendPID), canc->cancelAuthCode, len);
     910              : }
     911              : 
     912              : /*
     913              :  * Send a NegotiateProtocolVersion to the client.  This lets the client know
     914              :  * that they have either requested a newer minor protocol version than we are
     915              :  * able to speak, or at least one protocol option that we don't understand, or
     916              :  * possibly both. FrontendProtocol has already been set to the version
     917              :  * requested by the client or the highest version we know how to speak,
     918              :  * whichever is older. If the highest version that we know how to speak is too
     919              :  * old for the client, it can abandon the connection.
     920              :  *
     921              :  * We also include in the response a list of protocol options we didn't
     922              :  * understand.  This allows clients to include optional parameters that might
     923              :  * be present either in newer protocol versions or third-party protocol
     924              :  * extensions without fear of having to reconnect if those options are not
     925              :  * understood, while at the same time making certain that the client is aware
     926              :  * of which options were actually accepted.
     927              :  */
     928              : static void
     929            0 : SendNegotiateProtocolVersion(List *unrecognized_protocol_options)
     930              : {
     931              :     StringInfoData buf;
     932              :     ListCell   *lc;
     933              : 
     934            0 :     pq_beginmessage(&buf, PqMsg_NegotiateProtocolVersion);
     935            0 :     pq_sendint32(&buf, FrontendProtocol);
     936            0 :     pq_sendint32(&buf, list_length(unrecognized_protocol_options));
     937            0 :     foreach(lc, unrecognized_protocol_options)
     938            0 :         pq_sendstring(&buf, lfirst(lc));
     939            0 :     pq_endmessage(&buf);
     940              : 
     941              :     /* no need to flush, some other message will follow */
     942            0 : }
     943              : 
     944              : 
     945              : /*
     946              :  * SIGTERM while processing startup packet.
     947              :  *
     948              :  * Running proc_exit() from a signal handler would be quite unsafe.
     949              :  * However, since we have not yet touched shared memory, we can just
     950              :  * pull the plug and exit without running any atexit handlers.
     951              :  *
     952              :  * One might be tempted to try to send a message, or log one, indicating
     953              :  * why we are disconnecting.  However, that would be quite unsafe in itself.
     954              :  * Also, it seems undesirable to provide clues about the database's state
     955              :  * to a client that has not yet completed authentication, or even sent us
     956              :  * a startup packet.
     957              :  */
     958              : static void
     959            0 : process_startup_packet_die(SIGNAL_ARGS)
     960              : {
     961            0 :     _exit(1);
     962              : }
     963              : 
     964              : /*
     965              :  * Timeout while processing startup packet.
     966              :  * As for process_startup_packet_die(), we exit via _exit(1).
     967              :  */
     968              : static void
     969            0 : StartupPacketTimeoutHandler(void)
     970              : {
     971            0 :     _exit(1);
     972              : }
     973              : 
     974              : /*
     975              :  * Helper for the log_connections GUC check hook.
     976              :  *
     977              :  * `elemlist` is a listified version of the string input passed to the
     978              :  * log_connections GUC check hook, check_log_connections().
     979              :  * check_log_connections() is responsible for cleaning up `elemlist`.
     980              :  *
     981              :  * validate_log_connections_options() returns false if an error was
     982              :  * encountered and the GUC input could not be validated and true otherwise.
     983              :  *
     984              :  * `flags` returns the flags that should be stored in the log_connections GUC
     985              :  * by its assign hook.
     986              :  */
     987              : static bool
     988         1302 : validate_log_connections_options(List *elemlist, uint32 *flags)
     989              : {
     990              :     ListCell   *l;
     991              :     char       *item;
     992              : 
     993              :     /*
     994              :      * For backwards compatibility, we accept these tokens by themselves.
     995              :      *
     996              :      * Prior to PostgreSQL 18, log_connections was a boolean GUC that accepted
     997              :      * any unambiguous substring of 'true', 'false', 'yes', 'no', 'on', and
     998              :      * 'off'. Since log_connections became a list of strings in 18, we only
     999              :      * accept complete option strings.
    1000              :      */
    1001              :     static const struct config_enum_entry compat_options[] = {
    1002              :         {"off", 0},
    1003              :         {"false", 0},
    1004              :         {"no", 0},
    1005              :         {"0", 0},
    1006              :         {"on", LOG_CONNECTION_ON},
    1007              :         {"true", LOG_CONNECTION_ON},
    1008              :         {"yes", LOG_CONNECTION_ON},
    1009              :         {"1", LOG_CONNECTION_ON},
    1010              :     };
    1011              : 
    1012         1302 :     *flags = 0;
    1013              : 
    1014              :     /* If an empty string was passed, we're done */
    1015         1302 :     if (list_length(elemlist) == 0)
    1016         1181 :         return true;
    1017              : 
    1018              :     /*
    1019              :      * Now check for the backwards compatibility options. They must always be
    1020              :      * specified on their own, so we error out if the first option is a
    1021              :      * backwards compatibility option and other options are also specified.
    1022              :      */
    1023          121 :     item = linitial(elemlist);
    1024              : 
    1025         1085 :     for (size_t i = 0; i < lengthof(compat_options); i++)
    1026              :     {
    1027          965 :         struct config_enum_entry option = compat_options[i];
    1028              : 
    1029          965 :         if (pg_strcasecmp(item, option.name) != 0)
    1030          964 :             continue;
    1031              : 
    1032            1 :         if (list_length(elemlist) > 1)
    1033              :         {
    1034            0 :             GUC_check_errdetail("Cannot specify log_connections option \"%s\" in a list with other options.",
    1035              :                                 item);
    1036            1 :             return false;
    1037              :         }
    1038              : 
    1039            1 :         *flags = option.val;
    1040            1 :         return true;
    1041              :     }
    1042              : 
    1043              :     /* Now check the aspect options. The empty string was already handled */
    1044          277 :     foreach(l, elemlist)
    1045              :     {
    1046              :         static const struct config_enum_entry options[] = {
    1047              :             {"receipt", LOG_CONNECTION_RECEIPT},
    1048              :             {"authentication", LOG_CONNECTION_AUTHENTICATION},
    1049              :             {"authorization", LOG_CONNECTION_AUTHORIZATION},
    1050              :             {"setup_durations", LOG_CONNECTION_SETUP_DURATIONS},
    1051              :             {"all", LOG_CONNECTION_ALL},
    1052              :         };
    1053              : 
    1054          157 :         item = lfirst(l);
    1055          520 :         for (size_t i = 0; i < lengthof(options); i++)
    1056              :         {
    1057          520 :             struct config_enum_entry option = options[i];
    1058              : 
    1059          520 :             if (pg_strcasecmp(item, option.name) == 0)
    1060              :             {
    1061          157 :                 *flags |= option.val;
    1062          157 :                 goto next;
    1063              :             }
    1064              :         }
    1065              : 
    1066            0 :         GUC_check_errdetail("Invalid option \"%s\".", item);
    1067            0 :         return false;
    1068              : 
    1069          157 : next:   ;
    1070              :     }
    1071              : 
    1072          120 :     return true;
    1073              : }
    1074              : 
    1075              : 
    1076              : /*
    1077              :  * GUC check hook for log_connections
    1078              :  */
    1079              : bool
    1080         1302 : check_log_connections(char **newval, void **extra, GucSource source)
    1081              : {
    1082              :     uint32      flags;
    1083              :     char       *rawstring;
    1084              :     List       *elemlist;
    1085              :     bool        success;
    1086              : 
    1087              :     /* Need a modifiable copy of string */
    1088         1302 :     rawstring = pstrdup(*newval);
    1089              : 
    1090         1302 :     if (!SplitIdentifierString(rawstring, ',', &elemlist))
    1091              :     {
    1092            0 :         GUC_check_errdetail("Invalid list syntax in parameter \"%s\".", "log_connections");
    1093            0 :         pfree(rawstring);
    1094            0 :         list_free(elemlist);
    1095            0 :         return false;
    1096              :     }
    1097              : 
    1098              :     /* Validation logic is all in the helper */
    1099         1302 :     success = validate_log_connections_options(elemlist, &flags);
    1100              : 
    1101              :     /* Time for cleanup */
    1102         1302 :     pfree(rawstring);
    1103         1302 :     list_free(elemlist);
    1104              : 
    1105         1302 :     if (!success)
    1106            0 :         return false;
    1107              : 
    1108              :     /*
    1109              :      * We succeeded, so allocate `extra` and save the flags there for use by
    1110              :      * assign_log_connections().
    1111              :      */
    1112         1302 :     *extra = guc_malloc(LOG, sizeof(int));
    1113         1302 :     if (!*extra)
    1114            0 :         return false;
    1115         1302 :     *((int *) *extra) = flags;
    1116              : 
    1117         1302 :     return true;
    1118              : }
    1119              : 
    1120              : /*
    1121              :  * GUC assign hook for log_connections
    1122              :  */
    1123              : void
    1124         1298 : assign_log_connections(const char *newval, void *extra)
    1125              : {
    1126         1298 :     log_connections = *((int *) extra);
    1127         1298 : }
        

Generated by: LCOV version 2.0-1