Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * recovery_gen.c
4 : * Generator for recovery configuration
5 : *
6 : * Portions Copyright (c) 2011-2023, PostgreSQL Global Development Group
7 : *
8 : *-------------------------------------------------------------------------
9 : */
10 : #include "postgres_fe.h"
11 :
12 : #include "common/logging.h"
13 : #include "fe_utils/recovery_gen.h"
14 : #include "fe_utils/string_utils.h"
15 :
16 : static char *escape_quotes(const char *src);
17 :
18 : /*
19 : * Write recovery configuration contents into a fresh PQExpBuffer, and
20 : * return it.
21 : */
22 : PQExpBuffer
1292 alvherre 23 CBC 7 : GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
24 : {
25 : PQconninfoOption *connOptions;
26 : PQExpBufferData conninfo_buf;
27 : char *escaped;
28 : PQExpBuffer contents;
29 :
30 7 : Assert(pgconn != NULL);
31 :
32 7 : contents = createPQExpBuffer();
33 7 : if (!contents)
366 tgl 34 UBC 0 : pg_fatal("out of memory");
35 :
36 : /*
37 : * In PostgreSQL 12 and newer versions, standby_mode is gone, replaced by
38 : * standby.signal to trigger a standby state at recovery.
39 : */
1292 alvherre 40 CBC 7 : if (PQserverVersion(pgconn) < MINIMUM_VERSION_FOR_RECOVERY_GUC)
1292 alvherre 41 UBC 0 : appendPQExpBufferStr(contents, "standby_mode = 'on'\n");
42 :
1292 alvherre 43 CBC 7 : connOptions = PQconninfo(pgconn);
44 7 : if (connOptions == NULL)
366 tgl 45 UBC 0 : pg_fatal("out of memory");
46 :
1292 alvherre 47 CBC 7 : initPQExpBuffer(&conninfo_buf);
48 280 : for (PQconninfoOption *opt = connOptions; opt && opt->keyword; opt++)
49 : {
50 : /* Omit empty settings and those libpqwalreceiver overrides. */
51 273 : if (strcmp(opt->keyword, "replication") == 0 ||
52 266 : strcmp(opt->keyword, "dbname") == 0 ||
53 259 : strcmp(opt->keyword, "fallback_application_name") == 0 ||
54 252 : (opt->val == NULL) ||
55 111 : (opt->val != NULL && opt->val[0] == '\0'))
56 169 : continue;
57 :
58 : /* Separate key-value pairs with spaces */
59 104 : if (conninfo_buf.len != 0)
60 97 : appendPQExpBufferChar(&conninfo_buf, ' ');
61 :
62 : /*
63 : * Write "keyword=value" pieces, the value string is escaped and/or
64 : * quoted if necessary.
65 : */
66 104 : appendPQExpBuffer(&conninfo_buf, "%s=", opt->keyword);
67 104 : appendConnStrVal(&conninfo_buf, opt->val);
68 : }
69 7 : if (PQExpBufferDataBroken(conninfo_buf))
366 tgl 70 UBC 0 : pg_fatal("out of memory");
71 :
72 : /*
73 : * Escape the connection string, so that it can be put in the config file.
74 : * Note that this is different from the escaping of individual connection
75 : * options above!
76 : */
1292 alvherre 77 CBC 7 : escaped = escape_quotes(conninfo_buf.data);
78 7 : termPQExpBuffer(&conninfo_buf);
79 7 : appendPQExpBuffer(contents, "primary_conninfo = '%s'\n", escaped);
80 7 : free(escaped);
81 :
82 7 : if (replication_slot)
83 : {
84 : /* unescaped: ReplicationSlotValidateName allows [a-z0-9_] only */
85 1 : appendPQExpBuffer(contents, "primary_slot_name = '%s'\n",
86 : replication_slot);
87 : }
88 :
89 7 : if (PQExpBufferBroken(contents))
366 tgl 90 UBC 0 : pg_fatal("out of memory");
91 :
1292 alvherre 92 CBC 7 : PQconninfoFree(connOptions);
93 :
94 7 : return contents;
95 : }
96 :
97 : /*
98 : * Write the configuration file in the directory specified in target_dir,
99 : * with the contents already collected in memory appended. Then write
100 : * the signal file into the target_dir. If the server does not support
101 : * recovery parameters as GUCs, the signal file is not necessary, and
102 : * configuration is written to recovery.conf.
103 : */
104 : void
105 5 : WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)
106 : {
107 : char filename[MAXPGPATH];
108 : FILE *cf;
109 : bool use_recovery_conf;
110 :
111 5 : Assert(pgconn != NULL);
112 :
113 5 : use_recovery_conf =
114 5 : PQserverVersion(pgconn) < MINIMUM_VERSION_FOR_RECOVERY_GUC;
115 :
116 5 : snprintf(filename, MAXPGPATH, "%s/%s", target_dir,
117 : use_recovery_conf ? "recovery.conf" : "postgresql.auto.conf");
118 :
1152 fujii 119 5 : cf = fopen(filename, use_recovery_conf ? "w" : "a");
1292 alvherre 120 5 : if (cf == NULL)
366 tgl 121 UBC 0 : pg_fatal("could not open file \"%s\": %m", filename);
122 :
1292 alvherre 123 CBC 5 : if (fwrite(contents->data, contents->len, 1, cf) != 1)
366 tgl 124 UBC 0 : pg_fatal("could not write to file \"%s\": %m", filename);
125 :
1292 alvherre 126 CBC 5 : fclose(cf);
127 :
128 5 : if (!use_recovery_conf)
129 : {
130 5 : snprintf(filename, MAXPGPATH, "%s/%s", target_dir, "standby.signal");
131 5 : cf = fopen(filename, "w");
132 5 : if (cf == NULL)
366 tgl 133 UBC 0 : pg_fatal("could not create file \"%s\": %m", filename);
134 :
1292 alvherre 135 CBC 5 : fclose(cf);
136 : }
137 5 : }
138 :
139 : /*
140 : * Escape a string so that it can be used as a value in a key-value pair
141 : * a configuration file.
142 : */
143 : static char *
144 7 : escape_quotes(const char *src)
145 : {
146 7 : char *result = escape_single_quotes_ascii(src);
147 :
148 7 : if (!result)
366 tgl 149 UBC 0 : pg_fatal("out of memory");
1292 alvherre 150 CBC 7 : return result;
151 : }
|