|
34 | 34 | #include "tcop/backend_startup.h"
|
35 | 35 | #include "tcop/tcopprot.h"
|
36 | 36 | #include "utils/builtins.h"
|
| 37 | +#include "utils/guc_hooks.h" |
37 | 38 | #include "utils/injection_point.h"
|
38 | 39 | #include "utils/memutils.h"
|
39 | 40 | #include "utils/ps_status.h"
|
40 | 41 | #include "utils/timeout.h"
|
| 42 | +#include "utils/varlena.h" |
41 | 43 |
|
42 | 44 | /* GUCs */
|
43 | 45 | bool Trace_connection_negotiation = false;
|
| 46 | +uint32 log_connections = 0; |
| 47 | +char *log_connections_string = NULL; |
44 | 48 |
|
45 | 49 | static void BackendInitialize(ClientSocket *client_sock, CAC_state cac);
|
46 | 50 | static int ProcessSSLStartup(Port *port);
|
47 | 51 | static int ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done);
|
48 | 52 | static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
|
49 | 53 | static void process_startup_packet_die(SIGNAL_ARGS);
|
50 | 54 | static void StartupPacketTimeoutHandler(void);
|
| 55 | +static bool validate_log_connections_options(List *elemlist, uint32 *flags); |
51 | 56 |
|
52 | 57 | /*
|
53 | 58 | * Entry point for a new backend process.
|
@@ -201,8 +206,8 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
|
201 | 206 | port->remote_host = MemoryContextStrdup(TopMemoryContext, remote_host);
|
202 | 207 | port->remote_port = MemoryContextStrdup(TopMemoryContext, remote_port);
|
203 | 208 |
|
204 |
| - /* And now we can issue the Log_connections message, if wanted */ |
205 |
| - if (Log_connections) |
| 209 | + /* And now we can log that the connection was received, if enabled */ |
| 210 | + if (log_connections & LOG_CONNECTION_RECEIPT) |
206 | 211 | {
|
207 | 212 | if (remote_port[0])
|
208 | 213 | ereport(LOG,
|
@@ -924,3 +929,155 @@ StartupPacketTimeoutHandler(void)
|
924 | 929 | {
|
925 | 930 | _exit(1);
|
926 | 931 | }
|
| 932 | + |
| 933 | +/* |
| 934 | + * Helper for the log_connections GUC check hook. |
| 935 | + * |
| 936 | + * `elemlist` is a listified version of the string input passed to the |
| 937 | + * log_connections GUC check hook, check_log_connections(). |
| 938 | + * check_log_connections() is responsible for cleaning up `elemlist`. |
| 939 | + * |
| 940 | + * validate_log_connections_options() returns false if an error was |
| 941 | + * encountered and the GUC input could not be validated and true otherwise. |
| 942 | + * |
| 943 | + * `flags` returns the flags that should be stored in the log_connections GUC |
| 944 | + * by its assign hook. |
| 945 | + */ |
| 946 | +static bool |
| 947 | +validate_log_connections_options(List *elemlist, uint32 *flags) |
| 948 | +{ |
| 949 | + ListCell *l; |
| 950 | + char *item; |
| 951 | + |
| 952 | + /* |
| 953 | + * For backwards compatibility, we accept these tokens by themselves. |
| 954 | + * |
| 955 | + * Prior to PostgreSQL 18, log_connections was a boolean GUC that accepted |
| 956 | + * any unambiguous substring of 'true', 'false', 'yes', 'no', 'on', and |
| 957 | + * 'off'. Since log_connections became a list of strings in 18, we only |
| 958 | + * accept complete option strings. |
| 959 | + */ |
| 960 | + static const struct config_enum_entry compat_options[] = { |
| 961 | + {"off", 0}, |
| 962 | + {"false", 0}, |
| 963 | + {"no", 0}, |
| 964 | + {"0", 0}, |
| 965 | + {"on", LOG_CONNECTION_ON}, |
| 966 | + {"true", LOG_CONNECTION_ON}, |
| 967 | + {"yes", LOG_CONNECTION_ON}, |
| 968 | + {"1", LOG_CONNECTION_ON}, |
| 969 | + }; |
| 970 | + |
| 971 | + *flags = 0; |
| 972 | + |
| 973 | + /* If an empty string was passed, we're done */ |
| 974 | + if (list_length(elemlist) == 0) |
| 975 | + return true; |
| 976 | + |
| 977 | + /* |
| 978 | + * Now check for the backwards compatibility options. They must always be |
| 979 | + * specified on their own, so we error out if the first option is a |
| 980 | + * backwards compatibility option and other options are also specified. |
| 981 | + */ |
| 982 | + item = linitial(elemlist); |
| 983 | + |
| 984 | + for (size_t i = 0; i < lengthof(compat_options); i++) |
| 985 | + { |
| 986 | + struct config_enum_entry option = compat_options[i]; |
| 987 | + |
| 988 | + if (pg_strcasecmp(item, option.name) != 0) |
| 989 | + continue; |
| 990 | + |
| 991 | + if (list_length(elemlist) > 1) |
| 992 | + { |
| 993 | + GUC_check_errdetail("Cannot specify log_connections option \"%s\" in a list with other options.", |
| 994 | + item); |
| 995 | + return false; |
| 996 | + } |
| 997 | + |
| 998 | + *flags = option.val; |
| 999 | + return true; |
| 1000 | + } |
| 1001 | + |
| 1002 | + /* Now check the aspect options. The empty string was already handled */ |
| 1003 | + foreach(l, elemlist) |
| 1004 | + { |
| 1005 | + static const struct config_enum_entry options[] = { |
| 1006 | + {"receipt", LOG_CONNECTION_RECEIPT}, |
| 1007 | + {"authentication", LOG_CONNECTION_AUTHENTICATION}, |
| 1008 | + {"authorization", LOG_CONNECTION_AUTHORIZATION}, |
| 1009 | + {"all", LOG_CONNECTION_ALL}, |
| 1010 | + }; |
| 1011 | + |
| 1012 | + item = lfirst(l); |
| 1013 | + for (size_t i = 0; i < lengthof(options); i++) |
| 1014 | + { |
| 1015 | + struct config_enum_entry option = options[i]; |
| 1016 | + |
| 1017 | + if (pg_strcasecmp(item, option.name) == 0) |
| 1018 | + { |
| 1019 | + *flags |= option.val; |
| 1020 | + goto next; |
| 1021 | + } |
| 1022 | + } |
| 1023 | + |
| 1024 | + GUC_check_errdetail("Invalid option \"%s\".", item); |
| 1025 | + return false; |
| 1026 | + |
| 1027 | +next: ; |
| 1028 | + } |
| 1029 | + |
| 1030 | + return true; |
| 1031 | +} |
| 1032 | + |
| 1033 | + |
| 1034 | +/* |
| 1035 | + * GUC check hook for log_connections |
| 1036 | + */ |
| 1037 | +bool |
| 1038 | +check_log_connections(char **newval, void **extra, GucSource source) |
| 1039 | +{ |
| 1040 | + uint32 flags; |
| 1041 | + char *rawstring; |
| 1042 | + List *elemlist; |
| 1043 | + bool success; |
| 1044 | + |
| 1045 | + /* Need a modifiable copy of string */ |
| 1046 | + rawstring = pstrdup(*newval); |
| 1047 | + |
| 1048 | + if (!SplitIdentifierString(rawstring, ',', &elemlist)) |
| 1049 | + { |
| 1050 | + GUC_check_errdetail("Invalid list syntax in parameter \"log_connections\"."); |
| 1051 | + pfree(rawstring); |
| 1052 | + list_free(elemlist); |
| 1053 | + return false; |
| 1054 | + } |
| 1055 | + |
| 1056 | + /* Validation logic is all in the helper */ |
| 1057 | + success = validate_log_connections_options(elemlist, &flags); |
| 1058 | + |
| 1059 | + /* Time for cleanup */ |
| 1060 | + pfree(rawstring); |
| 1061 | + list_free(elemlist); |
| 1062 | + |
| 1063 | + if (!success) |
| 1064 | + return false; |
| 1065 | + |
| 1066 | + /* |
| 1067 | + * We succeeded, so allocate `extra` and save the flags there for use by |
| 1068 | + * assign_log_connections(). |
| 1069 | + */ |
| 1070 | + *extra = guc_malloc(ERROR, sizeof(int)); |
| 1071 | + *((int *) *extra) = flags; |
| 1072 | + |
| 1073 | + return true; |
| 1074 | +} |
| 1075 | + |
| 1076 | +/* |
| 1077 | + * GUC assign hook for log_connections |
| 1078 | + */ |
| 1079 | +void |
| 1080 | +assign_log_connections(const char *newval, void *extra) |
| 1081 | +{ |
| 1082 | + log_connections = *((int *) extra); |
| 1083 | +} |
0 commit comments