/src/postgres/src/backend/tcop/backend_startup.c
Line | Count | Source |
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 | | BackendMain(const void *startup_data, size_t startup_data_len) |
77 | 0 | { |
78 | 0 | const BackendStartupData *bsdata = startup_data; |
79 | |
|
80 | 0 | Assert(startup_data_len == sizeof(BackendStartupData)); |
81 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | MemoryContextSwitchTo(TopMemoryContext); |
123 | |
|
124 | 0 | PostgresMain(MyProcPort->database_name, MyProcPort->user_name); |
125 | 0 | } |
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 | | BackendInitialize(ClientSocket *client_sock, CAC_state cac) |
142 | 0 | { |
143 | 0 | int status; |
144 | 0 | int ret; |
145 | 0 | Port *port; |
146 | 0 | char remote_host[NI_MAXHOST]; |
147 | 0 | char remote_port[NI_MAXSERV]; |
148 | 0 | StringInfoData ps_data; |
149 | 0 | MemoryContext oldcontext; |
150 | | |
151 | | /* Tell fd.c about the long-lived FD associated with the client_sock */ |
152 | 0 | 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 | 0 | if (PreAuthDelay > 0) |
162 | 0 | pg_usleep(PreAuthDelay * 1000000L); |
163 | | |
164 | | /* This flag will remain set until InitPostgres finishes authentication */ |
165 | 0 | 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 | 0 | oldcontext = MemoryContextSwitchTo(TopMemoryContext); |
177 | 0 | port = MyProcPort = pq_init(client_sock); |
178 | 0 | MemoryContextSwitchTo(oldcontext); |
179 | |
|
180 | 0 | 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 | 0 | port->remote_host = ""; |
184 | 0 | 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 | 0 | pqsignal(SIGTERM, process_startup_packet_die); |
197 | | /* SIGQUIT handler was already set up by InitPostmasterChild */ |
198 | 0 | InitializeTimeouts(); /* establishes SIGALRM handler */ |
199 | 0 | sigprocmask(SIG_SETMASK, &StartupBlockSig, NULL); |
200 | | |
201 | | /* |
202 | | * Get the remote host name and port for logging and status display. |
203 | | */ |
204 | 0 | remote_host[0] = '\0'; |
205 | 0 | remote_port[0] = '\0'; |
206 | 0 | if ((ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen, |
207 | 0 | remote_host, sizeof(remote_host), |
208 | 0 | remote_port, sizeof(remote_port), |
209 | 0 | (log_hostname ? 0 : NI_NUMERICHOST) | NI_NUMERICSERV)) != 0) |
210 | 0 | ereport(WARNING, |
211 | 0 | (errmsg_internal("pg_getnameinfo_all() failed: %s", |
212 | 0 | 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 | 0 | port->remote_host = MemoryContextStrdup(TopMemoryContext, remote_host); |
219 | 0 | port->remote_port = MemoryContextStrdup(TopMemoryContext, remote_port); |
220 | | |
221 | | /* And now we can log that the connection was received, if enabled */ |
222 | 0 | if (log_connections & LOG_CONNECTION_RECEIPT) |
223 | 0 | { |
224 | 0 | if (remote_port[0]) |
225 | 0 | ereport(LOG, |
226 | 0 | (errmsg("connection received: host=%s port=%s", |
227 | 0 | remote_host, |
228 | 0 | remote_port))); |
229 | 0 | else |
230 | 0 | ereport(LOG, |
231 | 0 | (errmsg("connection received: host=%s", |
232 | 0 | remote_host))); |
233 | 0 | } |
234 | | |
235 | | /* For testing client error handling */ |
236 | | #ifdef USE_INJECTION_POINTS |
237 | | INJECTION_POINT("backend-initialize", NULL); |
238 | | 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 | | FrontendProtocol = PG_PROTOCOL(2, 0); |
246 | | 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 | 0 | if (log_hostname && |
262 | 0 | ret == 0 && |
263 | 0 | strspn(remote_host, "0123456789.") < strlen(remote_host) && |
264 | 0 | strspn(remote_host, "0123456789ABCDEFabcdef:") < strlen(remote_host)) |
265 | 0 | { |
266 | 0 | port->remote_hostname = MemoryContextStrdup(TopMemoryContext, remote_host); |
267 | 0 | } |
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 | 0 | RegisterTimeout(STARTUP_PACKET_TIMEOUT, StartupPacketTimeoutHandler); |
285 | 0 | enable_timeout_after(STARTUP_PACKET_TIMEOUT, AuthenticationTimeout * 1000); |
286 | | |
287 | | /* Handle direct SSL handshake */ |
288 | 0 | status = ProcessSSLStartup(port); |
289 | | |
290 | | /* |
291 | | * Receive the startup packet (which might turn out to be a cancel request |
292 | | * packet). |
293 | | */ |
294 | 0 | if (status == STATUS_OK) |
295 | 0 | 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 | 0 | if (status == STATUS_OK) |
303 | 0 | { |
304 | 0 | switch (cac) |
305 | 0 | { |
306 | 0 | case CAC_STARTUP: |
307 | 0 | ereport(FATAL, |
308 | 0 | (errcode(ERRCODE_CANNOT_CONNECT_NOW), |
309 | 0 | errmsg("the database system is starting up"))); |
310 | 0 | break; |
311 | 0 | case CAC_NOTHOTSTANDBY: |
312 | 0 | if (!EnableHotStandby) |
313 | 0 | ereport(FATAL, |
314 | 0 | (errcode(ERRCODE_CANNOT_CONNECT_NOW), |
315 | 0 | errmsg("the database system is not accepting connections"), |
316 | 0 | errdetail("Hot standby mode is disabled."))); |
317 | 0 | else if (reachedConsistency) |
318 | 0 | ereport(FATAL, |
319 | 0 | (errcode(ERRCODE_CANNOT_CONNECT_NOW), |
320 | 0 | errmsg("the database system is not yet accepting connections"), |
321 | 0 | errdetail("Recovery snapshot is not yet ready for hot standby."), |
322 | 0 | errhint("To enable hot standby, close write transactions with more than %d subtransactions on the primary server.", |
323 | 0 | PGPROC_MAX_CACHED_SUBXIDS))); |
324 | 0 | else |
325 | 0 | ereport(FATAL, |
326 | 0 | (errcode(ERRCODE_CANNOT_CONNECT_NOW), |
327 | 0 | errmsg("the database system is not yet accepting connections"), |
328 | 0 | errdetail("Consistent recovery state has not been yet reached."))); |
329 | 0 | break; |
330 | 0 | case CAC_SHUTDOWN: |
331 | 0 | ereport(FATAL, |
332 | 0 | (errcode(ERRCODE_CANNOT_CONNECT_NOW), |
333 | 0 | errmsg("the database system is shutting down"))); |
334 | 0 | break; |
335 | 0 | case CAC_RECOVERY: |
336 | 0 | ereport(FATAL, |
337 | 0 | (errcode(ERRCODE_CANNOT_CONNECT_NOW), |
338 | 0 | errmsg("the database system is in recovery mode"))); |
339 | 0 | break; |
340 | 0 | case CAC_TOOMANY: |
341 | 0 | ereport(FATAL, |
342 | 0 | (errcode(ERRCODE_TOO_MANY_CONNECTIONS), |
343 | 0 | errmsg("sorry, too many clients already"))); |
344 | 0 | break; |
345 | 0 | case CAC_OK: |
346 | 0 | break; |
347 | 0 | } |
348 | 0 | } |
349 | | |
350 | | /* |
351 | | * Disable the timeout, and prevent SIGTERM again. |
352 | | */ |
353 | 0 | disable_timeout(STARTUP_PACKET_TIMEOUT, false); |
354 | 0 | 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 | 0 | 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 | 0 | if (status != STATUS_OK) |
372 | 0 | 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 | 0 | initStringInfo(&ps_data); |
379 | 0 | if (am_walsender) |
380 | 0 | appendStringInfo(&ps_data, "%s ", GetBackendTypeDesc(B_WAL_SENDER)); |
381 | 0 | appendStringInfo(&ps_data, "%s ", port->user_name); |
382 | 0 | if (port->database_name[0] != '\0') |
383 | 0 | appendStringInfo(&ps_data, "%s ", port->database_name); |
384 | 0 | appendStringInfoString(&ps_data, port->remote_host); |
385 | 0 | if (port->remote_port[0] != '\0') |
386 | 0 | appendStringInfo(&ps_data, "(%s)", port->remote_port); |
387 | |
|
388 | 0 | init_ps_display(ps_data.data); |
389 | 0 | pfree(ps_data.data); |
390 | |
|
391 | 0 | set_ps_display("initializing"); |
392 | 0 | } |
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 | | ProcessSSLStartup(Port *port) |
402 | | { |
403 | | int firstbyte; |
404 | | |
405 | | Assert(!port->ssl_in_use); |
406 | | |
407 | | pq_startmsgread(); |
408 | | firstbyte = pq_peekbyte(); |
409 | | pq_endmsgread(); |
410 | | 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 | | return STATUS_ERROR; |
417 | | } |
418 | | |
419 | | if (firstbyte != 0x16) |
420 | | { |
421 | | /* Not an SSL handshake message */ |
422 | | 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 | | if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX) |
434 | | { |
435 | | /* SSL not supported */ |
436 | | goto reject; |
437 | | } |
438 | | |
439 | | if (secure_open_server(port) == -1) |
440 | | { |
441 | | /* |
442 | | * we assume secure_open_server() sent an appropriate TLS alert |
443 | | * already |
444 | | */ |
445 | | goto reject; |
446 | | } |
447 | | Assert(port->ssl_in_use); |
448 | | |
449 | | if (!port->alpn_used) |
450 | | { |
451 | | ereport(COMMERROR, |
452 | | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
453 | | errmsg("received direct SSL connection request without ALPN protocol negotiation extension"))); |
454 | | goto reject; |
455 | | } |
456 | | |
457 | | if (Trace_connection_negotiation) |
458 | | ereport(LOG, |
459 | | (errmsg("direct SSL connection accepted"))); |
460 | | return STATUS_OK; |
461 | | #else |
462 | | /* SSL not supported by this build */ |
463 | | goto reject; |
464 | | #endif |
465 | | |
466 | | reject: |
467 | | if (Trace_connection_negotiation) |
468 | | ereport(LOG, |
469 | | (errmsg("direct SSL connection rejected"))); |
470 | | 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 | | ProcessStartupPacket(Port *port) |
487 | 0 | { |
488 | 0 | int32 len; |
489 | 0 | char *buf = NULL; |
490 | 0 | ProtocolVersion proto; |
491 | 0 | MemoryContext oldcontext; |
492 | 0 | bool gss_done; |
493 | 0 | 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 | 0 | gss_done = false; |
504 | 0 | ssl_done = false; |
505 | |
|
506 | 0 | retry: |
507 | 0 | 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 | 0 | if (pq_getbytes(&len, 1) == EOF) |
516 | 0 | { |
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 | 0 | goto fail; |
528 | 0 | } |
529 | | |
530 | 0 | if (pq_getbytes(((char *) &len) + 1, 3) == EOF) |
531 | 0 | { |
532 | | /* Got a partial length word, so bleat about that */ |
533 | 0 | if (!ssl_done && !gss_done) |
534 | 0 | ereport(COMMERROR, |
535 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
536 | 0 | errmsg("incomplete startup packet"))); |
537 | 0 | goto fail; |
538 | 0 | } |
539 | | |
540 | 0 | len = pg_ntoh32(len); |
541 | 0 | len -= 4; |
542 | |
|
543 | 0 | if (len < (int32) sizeof(ProtocolVersion) || |
544 | 0 | len > MAX_STARTUP_PACKET_LENGTH) |
545 | 0 | { |
546 | 0 | ereport(COMMERROR, |
547 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
548 | 0 | errmsg("invalid length of startup packet"))); |
549 | 0 | goto fail; |
550 | 0 | } |
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 | 0 | buf = palloc(len + 1); |
558 | 0 | buf[len] = '\0'; |
559 | |
|
560 | 0 | if (pq_getbytes(buf, len) == EOF) |
561 | 0 | { |
562 | 0 | ereport(COMMERROR, |
563 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
564 | 0 | errmsg("incomplete startup packet"))); |
565 | 0 | goto fail; |
566 | 0 | } |
567 | 0 | pq_endmsgread(); |
568 | | |
569 | | /* |
570 | | * The first field is either a protocol version number or a special |
571 | | * request code. |
572 | | */ |
573 | 0 | port->proto = proto = pg_ntoh32(*((ProtocolVersion *) buf)); |
574 | |
|
575 | 0 | if (proto == CANCEL_REQUEST_CODE) |
576 | 0 | { |
577 | 0 | ProcessCancelRequestPacket(port, buf, len); |
578 | | /* Not really an error, but we don't want to proceed further */ |
579 | 0 | goto fail; |
580 | 0 | } |
581 | | |
582 | 0 | if (proto == NEGOTIATE_SSL_CODE && !ssl_done) |
583 | 0 | { |
584 | 0 | 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 | | if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX || port->ssl_in_use) |
594 | | SSLok = 'N'; |
595 | | else |
596 | | SSLok = 'S'; /* Support for SSL */ |
597 | | #else |
598 | 0 | SSLok = 'N'; /* No support for SSL */ |
599 | 0 | #endif |
600 | |
|
601 | 0 | if (Trace_connection_negotiation) |
602 | 0 | { |
603 | 0 | if (SSLok == 'S') |
604 | 0 | ereport(LOG, |
605 | 0 | (errmsg("SSLRequest accepted"))); |
606 | 0 | else |
607 | 0 | ereport(LOG, |
608 | 0 | (errmsg("SSLRequest rejected"))); |
609 | 0 | } |
610 | | |
611 | 0 | while (secure_write(port, &SSLok, 1) != 1) |
612 | 0 | { |
613 | 0 | if (errno == EINTR) |
614 | 0 | continue; /* if interrupted, just retry */ |
615 | 0 | ereport(COMMERROR, |
616 | 0 | (errcode_for_socket_access(), |
617 | 0 | errmsg("failed to send SSL negotiation response: %m"))); |
618 | 0 | goto fail; /* close the connection */ |
619 | 0 | } |
620 | | |
621 | | #ifdef USE_SSL |
622 | | if (SSLok == 'S' && secure_open_server(port) == -1) |
623 | | goto fail; |
624 | | #endif |
625 | | |
626 | 0 | pfree(buf); |
627 | 0 | 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 | 0 | if (pq_buffer_remaining_data() > 0) |
636 | 0 | ereport(FATAL, |
637 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
638 | 0 | errmsg("received unencrypted data after SSL request"), |
639 | 0 | 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 | 0 | ssl_done = true; |
647 | 0 | if (SSLok == 'S') |
648 | 0 | { |
649 | | /* |
650 | | * We are done with SSL and negotiated correctly, so consider the |
651 | | * same for GSS. |
652 | | */ |
653 | 0 | gss_done = true; |
654 | 0 | } |
655 | 0 | goto retry; |
656 | 0 | } |
657 | 0 | else if (proto == NEGOTIATE_GSS_CODE && !gss_done) |
658 | 0 | { |
659 | 0 | 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 | 0 | if (Trace_connection_negotiation) |
673 | 0 | { |
674 | 0 | if (GSSok == 'G') |
675 | 0 | ereport(LOG, |
676 | 0 | (errmsg("GSSENCRequest accepted"))); |
677 | 0 | else |
678 | 0 | ereport(LOG, |
679 | 0 | (errmsg("GSSENCRequest rejected"))); |
680 | 0 | } |
681 | | |
682 | 0 | while (secure_write(port, &GSSok, 1) != 1) |
683 | 0 | { |
684 | 0 | if (errno == EINTR) |
685 | 0 | continue; |
686 | 0 | ereport(COMMERROR, |
687 | 0 | (errcode_for_socket_access(), |
688 | 0 | errmsg("failed to send GSSAPI negotiation response: %m"))); |
689 | 0 | goto fail; /* close the connection */ |
690 | 0 | } |
691 | | |
692 | | #ifdef ENABLE_GSS |
693 | | if (GSSok == 'G' && secure_open_gssapi(port) == -1) |
694 | | goto fail; |
695 | | #endif |
696 | | |
697 | 0 | pfree(buf); |
698 | 0 | 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 | 0 | if (pq_buffer_remaining_data() > 0) |
707 | 0 | ereport(FATAL, |
708 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
709 | 0 | errmsg("received unencrypted data after GSSAPI encryption request"), |
710 | 0 | 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 | 0 | gss_done = true; |
718 | 0 | if (GSSok == 'G') |
719 | 0 | { |
720 | | /* |
721 | | * We are done with GSS and negotiated correctly, so consider the |
722 | | * same for SSL. |
723 | | */ |
724 | 0 | ssl_done = true; |
725 | 0 | } |
726 | 0 | goto retry; |
727 | 0 | } |
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 | 0 | FrontendProtocol = Min(proto, PG_PROTOCOL_LATEST); |
740 | | |
741 | | /* Check that the major protocol version is in range. */ |
742 | 0 | if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) || |
743 | 0 | PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST)) |
744 | 0 | ereport(FATAL, |
745 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
746 | 0 | errmsg("unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u", |
747 | 0 | PG_PROTOCOL_MAJOR(proto), PG_PROTOCOL_MINOR(proto), |
748 | 0 | PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST), |
749 | 0 | PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST), |
750 | 0 | 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 | 0 | oldcontext = MemoryContextSwitchTo(TopMemoryContext); |
757 | | |
758 | | /* Handle protocol version 3 startup packet */ |
759 | 0 | { |
760 | 0 | int32 offset = sizeof(ProtocolVersion); |
761 | 0 | 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 | 0 | port->guc_options = NIL; |
769 | |
|
770 | 0 | while (offset < len) |
771 | 0 | { |
772 | 0 | char *nameptr = buf + offset; |
773 | 0 | int32 valoffset; |
774 | 0 | char *valptr; |
775 | |
|
776 | 0 | if (*nameptr == '\0') |
777 | 0 | break; /* found packet terminator */ |
778 | 0 | valoffset = offset + strlen(nameptr) + 1; |
779 | 0 | if (valoffset >= len) |
780 | 0 | break; /* missing value, will complain below */ |
781 | 0 | valptr = buf + valoffset; |
782 | |
|
783 | 0 | if (strcmp(nameptr, "database") == 0) |
784 | 0 | port->database_name = pstrdup(valptr); |
785 | 0 | else if (strcmp(nameptr, "user") == 0) |
786 | 0 | port->user_name = pstrdup(valptr); |
787 | 0 | else if (strcmp(nameptr, "options") == 0) |
788 | 0 | port->cmdline_options = pstrdup(valptr); |
789 | 0 | else if (strcmp(nameptr, "replication") == 0) |
790 | 0 | { |
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 | 0 | if (strcmp(valptr, "database") == 0) |
799 | 0 | { |
800 | 0 | am_walsender = true; |
801 | 0 | am_db_walsender = true; |
802 | 0 | } |
803 | 0 | else if (!parse_bool(valptr, &am_walsender)) |
804 | 0 | ereport(FATAL, |
805 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
806 | 0 | errmsg("invalid value for parameter \"%s\": \"%s\"", |
807 | 0 | "replication", |
808 | 0 | valptr), |
809 | 0 | errhint("Valid values are: \"false\", 0, \"true\", 1, \"database\"."))); |
810 | 0 | } |
811 | 0 | else if (strncmp(nameptr, "_pq_.", 5) == 0) |
812 | 0 | { |
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 | 0 | unrecognized_protocol_options = |
819 | 0 | lappend(unrecognized_protocol_options, pstrdup(nameptr)); |
820 | 0 | } |
821 | 0 | else |
822 | 0 | { |
823 | | /* Assume it's a generic GUC option */ |
824 | 0 | port->guc_options = lappend(port->guc_options, |
825 | 0 | pstrdup(nameptr)); |
826 | 0 | port->guc_options = lappend(port->guc_options, |
827 | 0 | 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 | 0 | if (strcmp(nameptr, "application_name") == 0) |
836 | 0 | { |
837 | 0 | port->application_name = pg_clean_ascii(valptr, 0); |
838 | 0 | } |
839 | 0 | } |
840 | 0 | offset = valoffset + strlen(valptr) + 1; |
841 | 0 | } |
842 | | |
843 | | /* |
844 | | * If we didn't find a packet terminator exactly at the end of the |
845 | | * given packet length, complain. |
846 | | */ |
847 | 0 | if (offset != len - 1) |
848 | 0 | ereport(FATAL, |
849 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
850 | 0 | 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 | 0 | if (PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST) || |
859 | 0 | unrecognized_protocol_options != NIL) |
860 | 0 | SendNegotiateProtocolVersion(unrecognized_protocol_options); |
861 | |
|
862 | 0 | list_free_deep(unrecognized_protocol_options); |
863 | 0 | } |
864 | | |
865 | | /* Check a user name was given. */ |
866 | 0 | if (port->user_name == NULL || port->user_name[0] == '\0') |
867 | 0 | ereport(FATAL, |
868 | 0 | (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), |
869 | 0 | errmsg("no PostgreSQL user name specified in startup packet"))); |
870 | | |
871 | | /* The database defaults to the user name. */ |
872 | 0 | 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 | 0 | if (strlen(port->database_name) >= NAMEDATALEN) |
880 | 0 | port->database_name[NAMEDATALEN - 1] = '\0'; |
881 | 0 | if (strlen(port->user_name) >= NAMEDATALEN) |
882 | 0 | port->user_name[NAMEDATALEN - 1] = '\0'; |
883 | |
|
884 | 0 | Assert(MyBackendType == B_BACKEND || MyBackendType == B_DEAD_END_BACKEND); |
885 | 0 | if (am_walsender) |
886 | 0 | 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 | 0 | if (am_walsender && !am_db_walsender) |
897 | 0 | port->database_name[0] = '\0'; |
898 | | |
899 | | /* |
900 | | * Done filling the Port structure |
901 | | */ |
902 | 0 | MemoryContextSwitchTo(oldcontext); |
903 | |
|
904 | 0 | pfree(buf); |
905 | |
|
906 | 0 | return STATUS_OK; |
907 | | |
908 | 0 | fail: |
909 | | /* be tidy, just to avoid Valgrind complaints */ |
910 | 0 | if (buf) |
911 | 0 | pfree(buf); |
912 | |
|
913 | 0 | return STATUS_ERROR; |
914 | 0 | } |
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 | | ProcessCancelRequestPacket(Port *port, void *pkt, int pktlen) |
923 | | { |
924 | | CancelRequestPacket *canc; |
925 | | int len; |
926 | | |
927 | | if (pktlen < offsetof(CancelRequestPacket, cancelAuthCode)) |
928 | | { |
929 | | ereport(COMMERROR, |
930 | | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
931 | | errmsg("invalid length of cancel request packet"))); |
932 | | return; |
933 | | } |
934 | | len = pktlen - offsetof(CancelRequestPacket, cancelAuthCode); |
935 | | if (len == 0 || len > 256) |
936 | | { |
937 | | ereport(COMMERROR, |
938 | | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
939 | | errmsg("invalid length of cancel key in cancel request packet"))); |
940 | | return; |
941 | | } |
942 | | |
943 | | canc = (CancelRequestPacket *) pkt; |
944 | | 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 | | SendNegotiateProtocolVersion(List *unrecognized_protocol_options) |
965 | 0 | { |
966 | 0 | StringInfoData buf; |
967 | 0 | ListCell *lc; |
968 | |
|
969 | 0 | pq_beginmessage(&buf, PqMsg_NegotiateProtocolVersion); |
970 | 0 | pq_sendint32(&buf, FrontendProtocol); |
971 | 0 | pq_sendint32(&buf, list_length(unrecognized_protocol_options)); |
972 | 0 | foreach(lc, unrecognized_protocol_options) |
973 | 0 | pq_sendstring(&buf, lfirst(lc)); |
974 | 0 | pq_endmessage(&buf); |
975 | | |
976 | | /* no need to flush, some other message will follow */ |
977 | 0 | } |
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 | | process_startup_packet_die(SIGNAL_ARGS) |
995 | 0 | { |
996 | 0 | _exit(1); |
997 | 0 | } |
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 | | StartupPacketTimeoutHandler(void) |
1005 | 0 | { |
1006 | 0 | _exit(1); |
1007 | 0 | } |
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 | | validate_log_connections_options(List *elemlist, uint32 *flags) |
1024 | 2 | { |
1025 | 2 | ListCell *l; |
1026 | 2 | 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 | 2 | static const struct config_enum_entry compat_options[] = { |
1037 | 2 | {"off", 0}, |
1038 | 2 | {"false", 0}, |
1039 | 2 | {"no", 0}, |
1040 | 2 | {"0", 0}, |
1041 | 2 | {"on", LOG_CONNECTION_ON}, |
1042 | 2 | {"true", LOG_CONNECTION_ON}, |
1043 | 2 | {"yes", LOG_CONNECTION_ON}, |
1044 | 2 | {"1", LOG_CONNECTION_ON}, |
1045 | 2 | }; |
1046 | | |
1047 | 2 | *flags = 0; |
1048 | | |
1049 | | /* If an empty string was passed, we're done */ |
1050 | 2 | if (list_length(elemlist) == 0) |
1051 | 2 | 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 | 0 | item = linitial(elemlist); |
1059 | |
|
1060 | 0 | for (size_t i = 0; i < lengthof(compat_options); i++) |
1061 | 0 | { |
1062 | 0 | struct config_enum_entry option = compat_options[i]; |
1063 | |
|
1064 | 0 | if (pg_strcasecmp(item, option.name) != 0) |
1065 | 0 | continue; |
1066 | | |
1067 | 0 | if (list_length(elemlist) > 1) |
1068 | 0 | { |
1069 | 0 | GUC_check_errdetail("Cannot specify log_connections option \"%s\" in a list with other options.", |
1070 | 0 | item); |
1071 | 0 | return false; |
1072 | 0 | } |
1073 | | |
1074 | 0 | *flags = option.val; |
1075 | 0 | return true; |
1076 | 0 | } |
1077 | | |
1078 | | /* Now check the aspect options. The empty string was already handled */ |
1079 | 0 | foreach(l, elemlist) |
1080 | 0 | { |
1081 | 0 | static const struct config_enum_entry options[] = { |
1082 | 0 | {"receipt", LOG_CONNECTION_RECEIPT}, |
1083 | 0 | {"authentication", LOG_CONNECTION_AUTHENTICATION}, |
1084 | 0 | {"authorization", LOG_CONNECTION_AUTHORIZATION}, |
1085 | 0 | {"setup_durations", LOG_CONNECTION_SETUP_DURATIONS}, |
1086 | 0 | {"all", LOG_CONNECTION_ALL}, |
1087 | 0 | }; |
1088 | |
|
1089 | 0 | item = lfirst(l); |
1090 | 0 | for (size_t i = 0; i < lengthof(options); i++) |
1091 | 0 | { |
1092 | 0 | struct config_enum_entry option = options[i]; |
1093 | |
|
1094 | 0 | if (pg_strcasecmp(item, option.name) == 0) |
1095 | 0 | { |
1096 | 0 | *flags |= option.val; |
1097 | 0 | goto next; |
1098 | 0 | } |
1099 | 0 | } |
1100 | | |
1101 | 0 | GUC_check_errdetail("Invalid option \"%s\".", item); |
1102 | 0 | return false; |
1103 | | |
1104 | 0 | next: ; |
1105 | 0 | } |
1106 | | |
1107 | 0 | return true; |
1108 | 0 | } |
1109 | | |
1110 | | |
1111 | | /* |
1112 | | * GUC check hook for log_connections |
1113 | | */ |
1114 | | bool |
1115 | | check_log_connections(char **newval, void **extra, GucSource source) |
1116 | 2 | { |
1117 | 2 | uint32 flags; |
1118 | 2 | char *rawstring; |
1119 | 2 | List *elemlist; |
1120 | 2 | bool success; |
1121 | | |
1122 | | /* Need a modifiable copy of string */ |
1123 | 2 | rawstring = pstrdup(*newval); |
1124 | | |
1125 | 2 | if (!SplitIdentifierString(rawstring, ',', &elemlist)) |
1126 | 0 | { |
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 | 0 | } |
1132 | | |
1133 | | /* Validation logic is all in the helper */ |
1134 | 2 | success = validate_log_connections_options(elemlist, &flags); |
1135 | | |
1136 | | /* Time for cleanup */ |
1137 | 2 | pfree(rawstring); |
1138 | 2 | list_free(elemlist); |
1139 | | |
1140 | 2 | 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 | 2 | *extra = guc_malloc(LOG, sizeof(int)); |
1148 | 2 | if (!*extra) |
1149 | 0 | return false; |
1150 | 2 | *((int *) *extra) = flags; |
1151 | | |
1152 | 2 | return true; |
1153 | 2 | } |
1154 | | |
1155 | | /* |
1156 | | * GUC assign hook for log_connections |
1157 | | */ |
1158 | | void |
1159 | | assign_log_connections(const char *newval, void *extra) |
1160 | 2 | { |
1161 | 2 | log_connections = *((int *) extra); |
1162 | 2 | } |