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
23 14 : GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
24 : {
25 : PQconninfoOption *connOptions;
26 : PQExpBufferData conninfo_buf;
27 : char *escaped;
28 : PQExpBuffer contents;
29 :
30 : Assert(pgconn != NULL);
31 :
32 14 : contents = createPQExpBuffer();
33 14 : if (!contents)
34 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 : */
40 14 : if (PQserverVersion(pgconn) < MINIMUM_VERSION_FOR_RECOVERY_GUC)
41 0 : appendPQExpBufferStr(contents, "standby_mode = 'on'\n");
42 :
43 14 : connOptions = PQconninfo(pgconn);
44 14 : if (connOptions == NULL)
45 0 : pg_fatal("out of memory");
46 :
47 14 : initPQExpBuffer(&conninfo_buf);
48 574 : for (PQconninfoOption *opt = connOptions; opt && opt->keyword; opt++)
49 : {
50 : /* Omit empty settings and those libpqwalreceiver overrides. */
51 560 : if (strcmp(opt->keyword, "replication") == 0 ||
52 546 : strcmp(opt->keyword, "dbname") == 0 ||
53 532 : strcmp(opt->keyword, "fallback_application_name") == 0 ||
54 518 : (opt->val == NULL) ||
55 236 : (opt->val != NULL && opt->val[0] == '\0'))
56 338 : continue;
57 :
58 : /* Separate key-value pairs with spaces */
59 222 : if (conninfo_buf.len != 0)
60 208 : appendPQExpBufferChar(&conninfo_buf, ' ');
61 :
62 : /*
63 : * Write "keyword=value" pieces, the value string is escaped and/or
64 : * quoted if necessary.
65 : */
66 222 : appendPQExpBuffer(&conninfo_buf, "%s=", opt->keyword);
67 222 : appendConnStrVal(&conninfo_buf, opt->val);
68 : }
69 14 : if (PQExpBufferDataBroken(conninfo_buf))
70 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 : */
77 14 : escaped = escape_quotes(conninfo_buf.data);
78 14 : termPQExpBuffer(&conninfo_buf);
79 14 : appendPQExpBuffer(contents, "primary_conninfo = '%s'\n", escaped);
80 14 : free(escaped);
81 :
82 14 : if (replication_slot)
83 : {
84 : /* unescaped: ReplicationSlotValidateName allows [a-z0-9_] only */
85 2 : appendPQExpBuffer(contents, "primary_slot_name = '%s'\n",
86 : replication_slot);
87 : }
88 :
89 14 : if (PQExpBufferBroken(contents))
90 0 : pg_fatal("out of memory");
91 :
92 14 : PQconninfoFree(connOptions);
93 :
94 14 : 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 10 : WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)
106 : {
107 : char filename[MAXPGPATH];
108 : FILE *cf;
109 : bool use_recovery_conf;
110 :
111 : Assert(pgconn != NULL);
112 :
113 10 : use_recovery_conf =
114 10 : PQserverVersion(pgconn) < MINIMUM_VERSION_FOR_RECOVERY_GUC;
115 :
116 10 : snprintf(filename, MAXPGPATH, "%s/%s", target_dir,
117 : use_recovery_conf ? "recovery.conf" : "postgresql.auto.conf");
118 :
119 10 : cf = fopen(filename, use_recovery_conf ? "w" : "a");
120 10 : if (cf == NULL)
121 0 : pg_fatal("could not open file \"%s\": %m", filename);
122 :
123 10 : if (fwrite(contents->data, contents->len, 1, cf) != 1)
124 0 : pg_fatal("could not write to file \"%s\": %m", filename);
125 :
126 10 : fclose(cf);
127 :
128 10 : if (!use_recovery_conf)
129 : {
130 10 : snprintf(filename, MAXPGPATH, "%s/%s", target_dir, "standby.signal");
131 10 : cf = fopen(filename, "w");
132 10 : if (cf == NULL)
133 0 : pg_fatal("could not create file \"%s\": %m", filename);
134 :
135 10 : fclose(cf);
136 : }
137 10 : }
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 14 : escape_quotes(const char *src)
145 : {
146 14 : char *result = escape_single_quotes_ascii(src);
147 :
148 14 : if (!result)
149 0 : pg_fatal("out of memory");
150 14 : return result;
151 : }
|