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