Branch data 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);
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 : 15472 : BackendMain(const void *startup_data, size_t startup_data_len)
77 : : {
78 : 15472 : 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 : 15472 : 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 : 15174 : 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 : 15171 : MemoryContextSwitchTo(TopMemoryContext);
123 : :
124 : 15171 : 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 : 15472 : 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 : 15472 : 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 [ - + ]: 15472 : if (PreAuthDelay > 0)
162 : 0 : pg_usleep(PreAuthDelay * 1000000L);
163 : :
164 : : /* This flag will remain set until InitPostgres finishes authentication */
165 : 15472 : 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 : 15472 : oldcontext = MemoryContextSwitchTo(TopMemoryContext);
177 : 15472 : port = MyProcPort = pq_init(client_sock);
178 : 15472 : MemoryContextSwitchTo(oldcontext);
179 : :
180 : 15472 : 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 : 15472 : port->remote_host = "";
184 : 15472 : 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 : 15472 : pqsignal(SIGTERM, process_startup_packet_die);
197 : : /* SIGQUIT handler was already set up by InitPostmasterChild */
198 : 15472 : InitializeTimeouts(); /* establishes SIGALRM handler */
199 : 15472 : sigprocmask(SIG_SETMASK, &StartupBlockSig, NULL);
200 : :
201 : : /*
202 : : * Get the remote host name and port for logging and status display.
203 : : */
204 : 15472 : remote_host[0] = '\0';
205 : 15472 : remote_port[0] = '\0';
206 [ + + - + ]: 15472 : 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 : 15472 : port->remote_host = MemoryContextStrdup(TopMemoryContext, remote_host);
219 : 15472 : port->remote_port = MemoryContextStrdup(TopMemoryContext, remote_port);
220 : :
221 : : /* And now we can log that the connection was received, if enabled */
222 [ + + ]: 15472 : if (log_connections & LOG_CONNECTION_RECEIPT)
223 : : {
224 [ + + ]: 450 : if (remote_port[0])
225 [ + - ]: 192 : ereport(LOG,
226 : : (errmsg("connection received: host=%s port=%s",
227 : : remote_host,
228 : : remote_port)));
229 : : else
230 [ + - ]: 258 : 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 : 15472 : INJECTION_POINT("backend-initialize", NULL);
238 [ + + ]: 15471 : 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 : 1 : FrontendProtocol = PG_PROTOCOL(2, 0);
246 [ + - ]: 1 : 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 [ + + + - ]: 15470 : if (log_hostname &&
262 : 161 : ret == 0 &&
263 [ + - ]: 161 : strspn(remote_host, "0123456789.") < strlen(remote_host) &&
264 [ + - ]: 161 : strspn(remote_host, "0123456789ABCDEFabcdef:") < strlen(remote_host))
265 : : {
266 : 161 : 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 : 15470 : RegisterTimeout(STARTUP_PACKET_TIMEOUT, StartupPacketTimeoutHandler);
285 : 15470 : enable_timeout_after(STARTUP_PACKET_TIMEOUT, AuthenticationTimeout * 1000);
286 : :
287 : : /* Handle direct SSL handshake */
288 : 15470 : status = ProcessSSLStartup(port);
289 : :
290 : : /*
291 : : * Receive the startup packet (which might turn out to be a cancel request
292 : : * packet).
293 : : */
294 [ + + ]: 15470 : if (status == STATUS_OK)
295 : 15464 : status = ProcessStartupPacket(port);
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 [ + + ]: 15468 : if (status == STATUS_OK)
303 : : {
304 [ + + + + : 15400 : switch (cac)
+ + - ]
305 : : {
306 : 171 : case CAC_STARTUP:
307 [ + - ]: 171 : ereport(FATAL,
308 : : (errcode(ERRCODE_CANNOT_CONNECT_NOW),
309 : : errmsg("the database system is starting up")));
310 : : break;
311 : 7 : case CAC_NOTHOTSTANDBY:
312 [ - + ]: 7 : 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 [ - + ]: 7 : 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 [ + - ]: 7 : 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 : 2 : case CAC_RECOVERY:
336 [ + - ]: 2 : 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 : 15174 : case CAC_OK:
346 : 15174 : break;
347 : : }
348 : : }
349 : :
350 : : /*
351 : : * Disable the timeout, and prevent SIGTERM again.
352 : : */
353 : 15242 : disable_timeout(STARTUP_PACKET_TIMEOUT, false);
354 : 15242 : 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 : 15242 : 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 [ + + ]: 15242 : if (status != STATUS_OK)
372 : 68 : 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 : 15174 : initStringInfo(&ps_data);
379 [ + + ]: 15174 : if (am_walsender)
380 : 1335 : appendStringInfo(&ps_data, "%s ", GetBackendTypeDesc(B_WAL_SENDER));
381 : 15174 : appendStringInfo(&ps_data, "%s ", port->user_name);
382 [ + + ]: 15174 : if (port->database_name[0] != '\0')
383 : 14646 : appendStringInfo(&ps_data, "%s ", port->database_name);
384 : 15174 : appendStringInfoString(&ps_data, port->remote_host);
385 [ + + ]: 15174 : if (port->remote_port[0] != '\0')
386 : 286 : appendStringInfo(&ps_data, "(%s)", port->remote_port);
387 : :
388 : 15174 : init_ps_display(ps_data.data);
389 : 15174 : pfree(ps_data.data);
390 : :
391 : 15174 : set_ps_display("initializing");
392 : 15174 : }
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 : 15470 : ProcessSSLStartup(Port *port)
402 : : {
403 : : int firstbyte;
404 : :
405 : : Assert(!port->ssl_in_use);
406 : :
407 : 15470 : pq_startmsgread();
408 : 15470 : firstbyte = pq_peekbyte();
409 : 15470 : pq_endmsgread();
410 [ + + ]: 15470 : 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 [ + + ]: 15466 : if (firstbyte != 0x16)
420 : : {
421 : : /* Not an SSL handshake message */
422 : 15461 : 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 : : */
485 : : static int
486 : 15464 : ProcessStartupPacket(Port *port)
487 : : {
488 : : int32 len;
489 : 15464 : char *buf = NULL;
490 : : ProtocolVersion proto;
491 : : MemoryContext oldcontext;
492 : : bool gss_done;
493 : : bool ssl_done;
494 : :
495 : : /*
496 : : * Set ssl_done and/or gss_done when negotiation of an encrypted layer
497 : : * (currently, TLS or GSSAPI) is completed. A successful negotiation of
498 : : * either encryption layer sets both flags, but a rejected negotiation
499 : : * sets only the flag for that layer, since the client may wish to try the
500 : : * other one. We should make no assumption here about the order in which
501 : : * the client may make requests.
502 : : */
503 : 15464 : gss_done = false;
504 : 15464 : ssl_done = false;
505 : :
506 : 288 : retry:
507 : 15752 : pq_startmsgread();
508 : :
509 : : /*
510 : : * Grab the first byte of the length word separately, so that we can tell
511 : : * whether we have no data at all or an incomplete packet. (This might
512 : : * sound inefficient, but it's not really, because of buffering in
513 : : * pqcomm.c.)
514 : : */
515 [ + + ]: 15752 : if (pq_getbytes(&len, 1) == EOF)
516 : : {
517 : : /*
518 : : * If we get no data at all, don't clutter the log with a complaint;
519 : : * such cases often occur for legitimate reasons. An example is that
520 : : * we might be here after responding to NEGOTIATE_SSL_CODE, and if the
521 : : * client didn't like our response, it'll probably just drop the
522 : : * connection. Service-monitoring software also often just opens and
523 : : * closes a connection without sending anything. (So do port
524 : : * scanners, which may be less benign, but it's not really our job to
525 : : * notice those.)
526 : : */
527 : 15 : goto fail;
528 : : }
529 : :
530 [ - + ]: 15737 : if (pq_getbytes(((char *) &len) + 1, 3) == EOF)
531 : : {
532 : : /* Got a partial length word, so bleat about that */
533 [ # # # # ]: 0 : if (!ssl_done && !gss_done)
534 [ # # ]: 0 : ereport(COMMERROR,
535 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
536 : : errmsg("incomplete startup packet")));
537 : 0 : goto fail;
538 : : }
539 : :
540 : 15737 : len = pg_ntoh32(len);
541 : 15737 : len -= 4;
542 : :
543 [ + - ]: 15737 : if (len < (int32) sizeof(ProtocolVersion) ||
544 [ - + ]: 15737 : len > MAX_STARTUP_PACKET_LENGTH)
545 : : {
546 [ # # ]: 0 : ereport(COMMERROR,
547 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
548 : : errmsg("invalid length of startup packet")));
549 : 0 : goto fail;
550 : : }
551 : :
552 : : /*
553 : : * Allocate space to hold the startup packet, plus one extra byte that's
554 : : * initialized to be zero. This ensures we will have null termination of
555 : : * all strings inside the packet.
556 : : */
557 : 15737 : buf = palloc(len + 1);
558 : 15737 : buf[len] = '\0';
559 : :
560 [ - + ]: 15737 : if (pq_getbytes(buf, len) == EOF)
561 : : {
562 [ # # ]: 0 : ereport(COMMERROR,
563 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
564 : : errmsg("incomplete startup packet")));
565 : 0 : goto fail;
566 : : }
567 : 15737 : pq_endmsgread();
568 : :
569 : : /*
570 : : * The first field is either a protocol version number or a special
571 : : * request code.
572 : : */
573 : 15737 : port->proto = proto = pg_ntoh32(*((ProtocolVersion *) buf));
574 : :
575 [ + + ]: 15737 : if (proto == CANCEL_REQUEST_CODE)
576 : : {
577 : 16 : ProcessCancelRequestPacket(port, buf, len);
578 : : /* Not really an error, but we don't want to proceed further */
579 : 16 : goto fail;
580 : : }
581 : :
582 [ + + + + ]: 15721 : if (proto == NEGOTIATE_SSL_CODE && !ssl_done)
583 : : {
584 : : char SSLok;
585 : :
586 : : #ifdef USE_SSL
587 : :
588 : : /*
589 : : * No SSL when disabled or on Unix sockets.
590 : : *
591 : : * Also no SSL negotiation if we already have a direct SSL connection
592 : : */
593 [ + + + - : 319 : if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX || port->ssl_in_use)
- + ]
594 : 151 : SSLok = 'N';
595 : : else
596 : 168 : SSLok = 'S'; /* Support for SSL */
597 : : #else
598 : : SSLok = 'N'; /* No support for SSL */
599 : : #endif
600 : :
601 [ + + ]: 319 : if (Trace_connection_negotiation)
602 : : {
603 [ + + ]: 13 : if (SSLok == 'S')
604 [ + - ]: 8 : ereport(LOG,
605 : : (errmsg("SSLRequest accepted")));
606 : : else
607 [ + - ]: 5 : ereport(LOG,
608 : : (errmsg("SSLRequest rejected")));
609 : : }
610 : :
611 [ - + ]: 319 : while (secure_write(port, &SSLok, 1) != 1)
612 : : {
613 [ # # ]: 0 : if (errno == EINTR)
614 : 0 : continue; /* if interrupted, just retry */
615 [ # # ]: 0 : ereport(COMMERROR,
616 : : (errcode_for_socket_access(),
617 : : errmsg("failed to send SSL negotiation response: %m")));
618 : 31 : goto fail; /* close the connection */
619 : : }
620 : :
621 : : #ifdef USE_SSL
622 [ + + + + ]: 319 : if (SSLok == 'S' && secure_open_server(port) == -1)
623 : 31 : goto fail;
624 : : #endif
625 : :
626 : 287 : pfree(buf);
627 : 287 : buf = NULL;
628 : :
629 : : /*
630 : : * At this point we should have no data already buffered. If we do,
631 : : * it was received before we performed the SSL handshake, so it wasn't
632 : : * encrypted and indeed may have been injected by a man-in-the-middle.
633 : : * We report this case to the client.
634 : : */
635 [ - + ]: 287 : if (pq_buffer_remaining_data() > 0)
636 [ # # ]: 0 : ereport(FATAL,
637 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
638 : : errmsg("received unencrypted data after SSL request"),
639 : : errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack.")));
640 : :
641 : : /*
642 : : * regular startup packet, cancel, etc packet should follow, but not
643 : : * another SSL negotiation request, and a GSS request should only
644 : : * follow if SSL was rejected (client may negotiate in either order)
645 : : */
646 : 287 : ssl_done = true;
647 [ + + ]: 287 : if (SSLok == 'S')
648 : : {
649 : : /*
650 : : * We are done with SSL and negotiated correctly, so consider the
651 : : * same for GSS.
652 : : */
653 : 136 : gss_done = true;
654 : : }
655 : 287 : goto retry;
656 : : }
657 [ + + + - ]: 15402 : else if (proto == NEGOTIATE_GSS_CODE && !gss_done)
658 : : {
659 : 1 : char GSSok = 'N';
660 : :
661 : : #ifdef ENABLE_GSS
662 : :
663 : : /*
664 : : * No GSSAPI encryption when on Unix socket.
665 : : *
666 : : * Also no GSS negotiation if we already have a direct SSL connection.
667 : : */
668 : : if (port->laddr.addr.ss_family != AF_UNIX && !port->ssl_in_use)
669 : : GSSok = 'G';
670 : : #endif
671 : :
672 [ + - ]: 1 : if (Trace_connection_negotiation)
673 : : {
674 [ - + ]: 1 : if (GSSok == 'G')
675 [ # # ]: 0 : ereport(LOG,
676 : : (errmsg("GSSENCRequest accepted")));
677 : : else
678 [ + - ]: 1 : ereport(LOG,
679 : : (errmsg("GSSENCRequest rejected")));
680 : : }
681 : :
682 [ - + ]: 1 : while (secure_write(port, &GSSok, 1) != 1)
683 : : {
684 [ # # ]: 0 : if (errno == EINTR)
685 : 0 : continue;
686 [ # # ]: 0 : ereport(COMMERROR,
687 : : (errcode_for_socket_access(),
688 : : errmsg("failed to send GSSAPI negotiation response: %m")));
689 : 0 : goto fail; /* close the connection */
690 : : }
691 : :
692 : : #ifdef ENABLE_GSS
693 : : if (GSSok == 'G' && secure_open_gssapi(port) == -1)
694 : : goto fail;
695 : : #endif
696 : :
697 : 1 : pfree(buf);
698 : 1 : buf = NULL;
699 : :
700 : : /*
701 : : * At this point we should have no data already buffered. If we do,
702 : : * it was received before we performed the GSS handshake, so it wasn't
703 : : * encrypted and indeed may have been injected by a man-in-the-middle.
704 : : * We report this case to the client.
705 : : */
706 [ - + ]: 1 : if (pq_buffer_remaining_data() > 0)
707 [ # # ]: 0 : ereport(FATAL,
708 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
709 : : errmsg("received unencrypted data after GSSAPI encryption request"),
710 : : errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack.")));
711 : :
712 : : /*
713 : : * regular startup packet, cancel, etc packet should follow, but not
714 : : * another GSS negotiation request, and an SSL request should only
715 : : * follow if GSS was rejected (client may negotiate in either order)
716 : : */
717 : 1 : gss_done = true;
718 [ - + ]: 1 : if (GSSok == 'G')
719 : : {
720 : : /*
721 : : * We are done with GSS and negotiated correctly, so consider the
722 : : * same for SSL.
723 : : */
724 : 0 : ssl_done = true;
725 : : }
726 : 1 : goto retry;
727 : : }
728 : :
729 : : /* Could add additional special packet types here */
730 : :
731 : : /*
732 : : * Set FrontendProtocol now so that ereport() knows what format to send if
733 : : * we fail during startup. We use the protocol version requested by the
734 : : * client unless it's higher than the latest version we support. It's
735 : : * possible that error message fields might look different in newer
736 : : * protocol versions, but that's something those new clients should be
737 : : * able to deal with.
738 : : */
739 : 15401 : FrontendProtocol = Min(proto, PG_PROTOCOL_LATEST);
740 : :
741 : : /* Check that the major protocol version is in range. */
742 [ + - ]: 15401 : if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
743 [ + + ]: 15401 : PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST))
744 [ + - ]: 1 : ereport(FATAL,
745 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
746 : : errmsg("unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u",
747 : : PG_PROTOCOL_MAJOR(proto), PG_PROTOCOL_MINOR(proto),
748 : : PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST),
749 : : PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST),
750 : : PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))));
751 : :
752 : : /*
753 : : * Now fetch parameters out of startup packet and save them into the Port
754 : : * structure.
755 : : */
756 : 15400 : oldcontext = MemoryContextSwitchTo(TopMemoryContext);
757 : :
758 : : /* Handle protocol version 3 startup packet */
759 : : {
760 : 15400 : int32 offset = sizeof(ProtocolVersion);
761 : 15400 : List *unrecognized_protocol_options = NIL;
762 : :
763 : : /*
764 : : * Scan packet body for name/option pairs. We can assume any string
765 : : * beginning within the packet body is null-terminated, thanks to
766 : : * zeroing extra byte above.
767 : : */
768 : 15400 : port->guc_options = NIL;
769 : :
770 [ + - ]: 89635 : while (offset < len)
771 : : {
772 : 89635 : char *nameptr = buf + offset;
773 : : int32 valoffset;
774 : : char *valptr;
775 : :
776 [ + + ]: 89635 : if (*nameptr == '\0')
777 : 15400 : break; /* found packet terminator */
778 : 74235 : valoffset = offset + strlen(nameptr) + 1;
779 [ - + ]: 74235 : if (valoffset >= len)
780 : 0 : break; /* missing value, will complain below */
781 : 74235 : valptr = buf + valoffset;
782 : :
783 [ + + ]: 74235 : if (strcmp(nameptr, "database") == 0)
784 : 15400 : port->database_name = pstrdup(valptr);
785 [ + + ]: 58835 : else if (strcmp(nameptr, "user") == 0)
786 : 15400 : port->user_name = pstrdup(valptr);
787 [ + + ]: 43435 : else if (strcmp(nameptr, "options") == 0)
788 : 4630 : port->cmdline_options = pstrdup(valptr);
789 [ + + ]: 38805 : else if (strcmp(nameptr, "replication") == 0)
790 : : {
791 : : /*
792 : : * Due to backward compatibility concerns the replication
793 : : * parameter is a hybrid beast which allows the value to be
794 : : * either boolean or the string 'database'. The latter
795 : : * connects to a specific database which is e.g. required for
796 : : * logical decoding while.
797 : : */
798 [ + + ]: 1389 : if (strcmp(valptr, "database") == 0)
799 : : {
800 : 819 : am_walsender = true;
801 : 819 : am_db_walsender = true;
802 : : }
803 [ - + ]: 570 : else if (!parse_bool(valptr, &am_walsender))
804 [ # # ]: 0 : ereport(FATAL,
805 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
806 : : errmsg("invalid value for parameter \"%s\": \"%s\"",
807 : : "replication",
808 : : valptr),
809 : : errhint("Valid values are: \"false\", 0, \"true\", 1, \"database\".")));
810 : : }
811 [ + + ]: 37416 : else if (strncmp(nameptr, "_pq_.", 5) == 0)
812 : : {
813 : : /*
814 : : * Any option beginning with _pq_. is reserved for use as a
815 : : * protocol-level option, but at present no such options are
816 : : * defined.
817 : : */
818 : : unrecognized_protocol_options =
819 : 15379 : lappend(unrecognized_protocol_options, pstrdup(nameptr));
820 : : }
821 : : else
822 : : {
823 : : /* Assume it's a generic GUC option */
824 : 22037 : port->guc_options = lappend(port->guc_options,
825 : 22037 : pstrdup(nameptr));
826 : 22037 : port->guc_options = lappend(port->guc_options,
827 : 22037 : pstrdup(valptr));
828 : :
829 : : /*
830 : : * Copy application_name to port if we come across it. This
831 : : * is done so we can log the application_name in the
832 : : * connection authorization message. Note that the GUC would
833 : : * be used but we haven't gone through GUC setup yet.
834 : : */
835 [ + + ]: 22037 : if (strcmp(nameptr, "application_name") == 0)
836 : : {
837 : 15395 : port->application_name = pg_clean_ascii(valptr, 0);
838 : : }
839 : : }
840 : 74235 : offset = valoffset + strlen(valptr) + 1;
841 : : }
842 : :
843 : : /*
844 : : * If we didn't find a packet terminator exactly at the end of the
845 : : * given packet length, complain.
846 : : */
847 [ - + ]: 15400 : if (offset != len - 1)
848 [ # # ]: 0 : ereport(FATAL,
849 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
850 : : errmsg("invalid startup packet layout: expected terminator as last byte")));
851 : :
852 : : /*
853 : : * If the client requested a newer protocol version or if the client
854 : : * requested any protocol options we didn't recognize, let them know
855 : : * the newest minor protocol version we do support and the names of
856 : : * any unrecognized options.
857 : : */
858 [ + + - + ]: 15400 : if (PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST) ||
859 : : unrecognized_protocol_options != NIL)
860 : 15379 : SendNegotiateProtocolVersion(unrecognized_protocol_options);
861 : :
862 : 15400 : list_free_deep(unrecognized_protocol_options);
863 : : }
864 : :
865 : : /* Check a user name was given. */
866 [ + - - + ]: 15400 : if (port->user_name == NULL || port->user_name[0] == '\0')
867 [ # # ]: 0 : ereport(FATAL,
868 : : (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
869 : : errmsg("no PostgreSQL user name specified in startup packet")));
870 : :
871 : : /* The database defaults to the user name. */
872 [ + - - + ]: 15400 : if (port->database_name == NULL || port->database_name[0] == '\0')
873 : 0 : port->database_name = pstrdup(port->user_name);
874 : :
875 : : /*
876 : : * Truncate given database and user names to length of a Postgres name.
877 : : * This avoids lookup failures when overlength names are given.
878 : : */
879 [ - + ]: 15400 : if (strlen(port->database_name) >= NAMEDATALEN)
880 : 0 : port->database_name[NAMEDATALEN - 1] = '\0';
881 [ - + ]: 15400 : if (strlen(port->user_name) >= NAMEDATALEN)
882 : 0 : port->user_name[NAMEDATALEN - 1] = '\0';
883 : :
884 : : Assert(MyBackendType == B_BACKEND || MyBackendType == B_DEAD_END_BACKEND);
885 [ + + ]: 15400 : if (am_walsender)
886 : 1389 : MyBackendType = B_WAL_SENDER;
887 : :
888 : : /*
889 : : * Normal walsender backends, e.g. for streaming replication, are not
890 : : * connected to a particular database. But walsenders used for logical
891 : : * replication need to connect to a specific database. We allow streaming
892 : : * replication commands to be issued even if connected to a database as it
893 : : * can make sense to first make a basebackup and then stream changes
894 : : * starting from that.
895 : : */
896 [ + + + + ]: 15400 : if (am_walsender && !am_db_walsender)
897 : 570 : port->database_name[0] = '\0';
898 : :
899 : : /*
900 : : * Done filling the Port structure
901 : : */
902 : 15400 : MemoryContextSwitchTo(oldcontext);
903 : :
904 : 15400 : pfree(buf);
905 : :
906 : 15400 : return STATUS_OK;
907 : :
908 : 62 : fail:
909 : : /* be tidy, just to avoid Valgrind complaints */
910 [ + + ]: 62 : if (buf)
911 : 47 : pfree(buf);
912 : :
913 : 62 : return STATUS_ERROR;
914 : : }
915 : :
916 : : /*
917 : : * The client has sent a cancel request packet, not a normal
918 : : * start-a-new-connection packet. Perform the necessary processing. Nothing
919 : : * is sent back to the client.
920 : : */
921 : : static void
922 : 16 : ProcessCancelRequestPacket(Port *port, void *pkt, int pktlen)
923 : : {
924 : : CancelRequestPacket *canc;
925 : : int len;
926 : :
927 [ - + ]: 16 : if (pktlen < offsetof(CancelRequestPacket, cancelAuthCode))
928 : : {
929 [ # # ]: 0 : ereport(COMMERROR,
930 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
931 : : errmsg("invalid length of cancel request packet")));
932 : 0 : return;
933 : : }
934 : 16 : len = pktlen - offsetof(CancelRequestPacket, cancelAuthCode);
935 [ + - - + ]: 16 : if (len == 0 || len > 256)
936 : : {
937 [ # # ]: 0 : ereport(COMMERROR,
938 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
939 : : errmsg("invalid length of cancel key in cancel request packet")));
940 : 0 : return;
941 : : }
942 : :
943 : 16 : canc = (CancelRequestPacket *) pkt;
944 : 16 : SendCancelRequest(pg_ntoh32(canc->backendPID), canc->cancelAuthCode, len);
945 : : }
946 : :
947 : : /*
948 : : * Send a NegotiateProtocolVersion to the client. This lets the client know
949 : : * that they have either requested a newer minor protocol version than we are
950 : : * able to speak, or at least one protocol option that we don't understand, or
951 : : * possibly both. FrontendProtocol has already been set to the version
952 : : * requested by the client or the highest version we know how to speak,
953 : : * whichever is older. If the highest version that we know how to speak is too
954 : : * old for the client, it can abandon the connection.
955 : : *
956 : : * We also include in the response a list of protocol options we didn't
957 : : * understand. This allows clients to include optional parameters that might
958 : : * be present either in newer protocol versions or third-party protocol
959 : : * extensions without fear of having to reconnect if those options are not
960 : : * understood, while at the same time making certain that the client is aware
961 : : * of which options were actually accepted.
962 : : */
963 : : static void
964 : 15379 : SendNegotiateProtocolVersion(List *unrecognized_protocol_options)
965 : : {
966 : : StringInfoData buf;
967 : : ListCell *lc;
968 : :
969 : 15379 : pq_beginmessage(&buf, PqMsg_NegotiateProtocolVersion);
970 : 15379 : pq_sendint32(&buf, FrontendProtocol);
971 : 15379 : pq_sendint32(&buf, list_length(unrecognized_protocol_options));
972 [ + - + + : 30758 : foreach(lc, unrecognized_protocol_options)
+ + ]
973 : 15379 : pq_sendstring(&buf, lfirst(lc));
974 : 15379 : pq_endmessage(&buf);
975 : :
976 : : /* no need to flush, some other message will follow */
977 : 15379 : }
978 : :
979 : :
980 : : /*
981 : : * SIGTERM while processing startup packet.
982 : : *
983 : : * Running proc_exit() from a signal handler would be quite unsafe.
984 : : * However, since we have not yet touched shared memory, we can just
985 : : * pull the plug and exit without running any atexit handlers.
986 : : *
987 : : * One might be tempted to try to send a message, or log one, indicating
988 : : * why we are disconnecting. However, that would be quite unsafe in itself.
989 : : * Also, it seems undesirable to provide clues about the database's state
990 : : * to a client that has not yet completed authentication, or even sent us
991 : : * a startup packet.
992 : : */
993 : : static void
994 : 0 : process_startup_packet_die(SIGNAL_ARGS)
995 : : {
996 : 0 : _exit(1);
997 : : }
998 : :
999 : : /*
1000 : : * Timeout while processing startup packet.
1001 : : * As for process_startup_packet_die(), we exit via _exit(1).
1002 : : */
1003 : : static void
1004 : 0 : StartupPacketTimeoutHandler(void)
1005 : : {
1006 : 0 : _exit(1);
1007 : : }
1008 : :
1009 : : /*
1010 : : * Helper for the log_connections GUC check hook.
1011 : : *
1012 : : * `elemlist` is a listified version of the string input passed to the
1013 : : * log_connections GUC check hook, check_log_connections().
1014 : : * check_log_connections() is responsible for cleaning up `elemlist`.
1015 : : *
1016 : : * validate_log_connections_options() returns false if an error was
1017 : : * encountered and the GUC input could not be validated and true otherwise.
1018 : : *
1019 : : * `flags` returns the flags that should be stored in the log_connections GUC
1020 : : * by its assign hook.
1021 : : */
1022 : : static bool
1023 : 1467 : validate_log_connections_options(List *elemlist, uint32 *flags)
1024 : : {
1025 : : ListCell *l;
1026 : : char *item;
1027 : :
1028 : : /*
1029 : : * For backwards compatibility, we accept these tokens by themselves.
1030 : : *
1031 : : * Prior to PostgreSQL 18, log_connections was a boolean GUC that accepted
1032 : : * any unambiguous substring of 'true', 'false', 'yes', 'no', 'on', and
1033 : : * 'off'. Since log_connections became a list of strings in 18, we only
1034 : : * accept complete option strings.
1035 : : */
1036 : : static const struct config_enum_entry compat_options[] = {
1037 : : {"off", 0},
1038 : : {"false", 0},
1039 : : {"no", 0},
1040 : : {"0", 0},
1041 : : {"on", LOG_CONNECTION_ON},
1042 : : {"true", LOG_CONNECTION_ON},
1043 : : {"yes", LOG_CONNECTION_ON},
1044 : : {"1", LOG_CONNECTION_ON},
1045 : : };
1046 : :
1047 : 1467 : *flags = 0;
1048 : :
1049 : : /* If an empty string was passed, we're done */
1050 [ + + ]: 1467 : if (list_length(elemlist) == 0)
1051 : 1315 : return true;
1052 : :
1053 : : /*
1054 : : * Now check for the backwards compatibility options. They must always be
1055 : : * specified on their own, so we error out if the first option is a
1056 : : * backwards compatibility option and other options are also specified.
1057 : : */
1058 : 152 : item = linitial(elemlist);
1059 : :
1060 [ + + ]: 1364 : for (size_t i = 0; i < lengthof(compat_options); i++)
1061 : : {
1062 : 1213 : struct config_enum_entry option = compat_options[i];
1063 : :
1064 [ + + ]: 1213 : if (pg_strcasecmp(item, option.name) != 0)
1065 : 1212 : continue;
1066 : :
1067 [ - + ]: 1 : if (list_length(elemlist) > 1)
1068 : : {
1069 : 0 : GUC_check_errdetail("Cannot specify log_connections option \"%s\" in a list with other options.",
1070 : : item);
1071 : 1 : return false;
1072 : : }
1073 : :
1074 : 1 : *flags = option.val;
1075 : 1 : return true;
1076 : : }
1077 : :
1078 : : /* Now check the aspect options. The empty string was already handled */
1079 [ + - + + : 341 : foreach(l, elemlist)
+ + ]
1080 : : {
1081 : : static const struct config_enum_entry options[] = {
1082 : : {"receipt", LOG_CONNECTION_RECEIPT},
1083 : : {"authentication", LOG_CONNECTION_AUTHENTICATION},
1084 : : {"authorization", LOG_CONNECTION_AUTHORIZATION},
1085 : : {"setup_durations", LOG_CONNECTION_SETUP_DURATIONS},
1086 : : {"all", LOG_CONNECTION_ALL},
1087 : : };
1088 : :
1089 : 190 : item = lfirst(l);
1090 [ + - ]: 676 : for (size_t i = 0; i < lengthof(options); i++)
1091 : : {
1092 : 676 : struct config_enum_entry option = options[i];
1093 : :
1094 [ + + ]: 676 : if (pg_strcasecmp(item, option.name) == 0)
1095 : : {
1096 : 190 : *flags |= option.val;
1097 : 190 : goto next;
1098 : : }
1099 : : }
1100 : :
1101 : 0 : GUC_check_errdetail("Invalid option \"%s\".", item);
1102 : 0 : return false;
1103 : :
1104 : 190 : next: ;
1105 : : }
1106 : :
1107 : 151 : return true;
1108 : : }
1109 : :
1110 : :
1111 : : /*
1112 : : * GUC check hook for log_connections
1113 : : */
1114 : : bool
1115 : 1467 : check_log_connections(char **newval, void **extra, GucSource source)
1116 : : {
1117 : : uint32 flags;
1118 : : char *rawstring;
1119 : : List *elemlist;
1120 : : bool success;
1121 : :
1122 : : /* Need a modifiable copy of string */
1123 : 1467 : rawstring = pstrdup(*newval);
1124 : :
1125 [ - + ]: 1467 : if (!SplitIdentifierString(rawstring, ',', &elemlist))
1126 : : {
1127 : 0 : GUC_check_errdetail("Invalid list syntax in parameter \"%s\".", "log_connections");
1128 : 0 : pfree(rawstring);
1129 : 0 : list_free(elemlist);
1130 : 0 : return false;
1131 : : }
1132 : :
1133 : : /* Validation logic is all in the helper */
1134 : 1467 : success = validate_log_connections_options(elemlist, &flags);
1135 : :
1136 : : /* Time for cleanup */
1137 : 1467 : pfree(rawstring);
1138 : 1467 : list_free(elemlist);
1139 : :
1140 [ - + ]: 1467 : if (!success)
1141 : 0 : return false;
1142 : :
1143 : : /*
1144 : : * We succeeded, so allocate `extra` and save the flags there for use by
1145 : : * assign_log_connections().
1146 : : */
1147 : 1467 : *extra = guc_malloc(LOG, sizeof(int));
1148 [ - + ]: 1467 : if (!*extra)
1149 : 0 : return false;
1150 : 1467 : *((int *) *extra) = flags;
1151 : :
1152 : 1467 : return true;
1153 : : }
1154 : :
1155 : : /*
1156 : : * GUC assign hook for log_connections
1157 : : */
1158 : : void
1159 : 1463 : assign_log_connections(const char *newval, void *extra)
1160 : : {
1161 : 1463 : log_connections = *((int *) extra);
1162 : 1463 : }
|