1 /* -*-pgsql-c-*- */
2 /*
3 * $Header$
4 *
5 * pgpool-recovery: exec online recovery script from SELECT statement.
6 *
7 * Copyright (c) 2003-2019 PgPool Global Development Group
8 *
9 * Permission to use, copy, modify, and distribute this software and
10 * its documentation for any purpose and without fee is hereby
11 * granted, provided that the above copyright notice appear in all
12 * copies and that both that copyright notice and this permission
13 * notice appear in supporting documentation, and that the name of the
14 * author not be used in advertising or publicity pertaining to
15 * distribution of the software without specific, written prior
16 * permission. The author makes no representations about the
17 * suitability of this software for any purpose. It is provided "as
18 * is" without express or implied warranty.
19 */
20
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include "postgres.h"
25 #include "fmgr.h"
26 #include "miscadmin.h"
27 #include "executor/spi.h"
28 #include "funcapi.h"
29 #include "catalog/namespace.h"
30 #include "catalog/pg_proc.h"
31 #include "utils/syscache.h"
32 #include "utils/builtins.h" /* PostgreSQL 8.4 needs this for textout */
33 #include "utils/guc.h"
34 #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 90300)
35 #include "access/htup_details.h" /* PostgreSQL 9.3 or later needs this */
36 #endif
37
38 #define REMOTE_START_FILE "pgpool_remote_start"
39
40 #include <stdlib.h>
41
42 #ifdef PG_MODULE_MAGIC
43 PG_MODULE_MAGIC;
44 #endif
45
46 PG_FUNCTION_INFO_V1(pgpool_recovery);
47 PG_FUNCTION_INFO_V1(pgpool_remote_start);
48 PG_FUNCTION_INFO_V1(pgpool_pgctl);
49 PG_FUNCTION_INFO_V1(pgpool_switch_xlog);
50
51 extern Datum pgpool_recovery(PG_FUNCTION_ARGS);
52 extern Datum pgpool_remote_start(PG_FUNCTION_ARGS);
53 extern Datum pgpool_pgctl(PG_FUNCTION_ARGS);
54 extern Datum pgpool_switch_xlog(PG_FUNCTION_ARGS);
55
56 static char recovery_script[1024];
57 static char command_text[1024];
58
59 static Oid get_function_oid(const char *funcname, const char *argtype, const char *nspname);
60 char *Log_line_prefix = NULL;
61
62 Datum
pgpool_recovery(PG_FUNCTION_ARGS)63 pgpool_recovery(PG_FUNCTION_ARGS)
64 {
65 int r;
66 char *script = DatumGetCString(DirectFunctionCall1(textout,
67 PointerGetDatum(PG_GETARG_TEXT_P(0))));
68
69 char *remote_host = DatumGetCString(DirectFunctionCall1(textout,
70 PointerGetDatum(PG_GETARG_TEXT_P(1))));
71 char *remote_data_directory = DatumGetCString(DirectFunctionCall1(textout,
72 PointerGetDatum(PG_GETARG_TEXT_P(2))));
73
74 if (!superuser())
75 #ifdef ERRCODE_INSUFFICIENT_PRIVILEGE
76 ereport(ERROR,
77 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
78 (errmsg("must be superuser to use pgpool_recovery function"))));
79 #else
80 elog(ERROR, "must be superuser to use pgpool_recovery function");
81 #endif
82
83 if (PG_NARGS() >= 4) /* post pgpool-II 3.4 */
84 {
85 char *remote_port = DatumGetCString(DirectFunctionCall1(textout,
86 PointerGetDatum(PG_GETARG_TEXT_P(3))));
87 snprintf(recovery_script, sizeof(recovery_script), "\"%s/%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
88 DataDir, script, DataDir, remote_host,
89 remote_data_directory, remote_port);
90 }
91 else
92 {
93 snprintf(recovery_script, sizeof(recovery_script), "\"%s/%s\" \"%s\" \"%s\" \"%s\"",
94 DataDir, script, DataDir, remote_host,
95 remote_data_directory);
96 }
97
98 elog(DEBUG1, "recovery_script: %s", recovery_script);
99 r = system(recovery_script);
100
101 if (r != 0)
102 {
103 elog(ERROR, "pgpool_recovery failed");
104 }
105
106 PG_RETURN_BOOL(true);
107 }
108
109 Datum
pgpool_remote_start(PG_FUNCTION_ARGS)110 pgpool_remote_start(PG_FUNCTION_ARGS)
111 {
112 int r;
113 char *remote_host = DatumGetCString(DirectFunctionCall1(textout,
114 PointerGetDatum(PG_GETARG_TEXT_P(0))));
115 char *remote_data_directory = DatumGetCString(DirectFunctionCall1(textout,
116 PointerGetDatum(PG_GETARG_TEXT_P(1))));
117 if (!superuser())
118 #ifdef ERRCODE_INSUFFICIENT_PRIVILEGE
119 ereport(ERROR,
120 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
121 (errmsg("must be superuser to use pgpool_remote_start function"))));
122 #else
123 elog(ERROR, "must be superuser to use pgpool_remote_start function");
124 #endif
125
126 snprintf(recovery_script, sizeof(recovery_script),
127 "%s/%s %s %s", DataDir, REMOTE_START_FILE,
128 remote_host, remote_data_directory);
129 elog(DEBUG1, "recovery_script: %s", recovery_script);
130 r = system(recovery_script);
131
132 if (r != 0)
133 {
134 elog(ERROR, "pgpool_remote_start failed");
135 }
136
137 PG_RETURN_BOOL(true);
138 }
139
140 Datum
pgpool_pgctl(PG_FUNCTION_ARGS)141 pgpool_pgctl(PG_FUNCTION_ARGS)
142 {
143 int r;
144 char *action = DatumGetCString(DirectFunctionCall1(textout,
145 PointerGetDatum(PG_GETARG_TEXT_P(0))));
146 char *stop_mode = DatumGetCString(DirectFunctionCall1(textout,
147 PointerGetDatum(PG_GETARG_TEXT_P(1))));
148 char *pg_ctl;
149 char *data_directory;
150
151 if (!superuser())
152 #ifdef ERRCODE_INSUFFICIENT_PRIVILEGE
153 ereport(ERROR,
154 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
155 (errmsg("must be superuser to use pgpool_pgctl function"))));
156 #else
157 elog(ERROR, "must be superuser to use pgpool_pgctl function");
158 #endif
159
160 #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 90600)
161 pg_ctl = GetConfigOptionByName("pgpool.pg_ctl", NULL,false);
162 data_directory = GetConfigOptionByName("data_directory", NULL,false);
163 #else
164 pg_ctl = GetConfigOptionByName("pgpool.pg_ctl", NULL);
165 data_directory = GetConfigOptionByName("data_directory", NULL);
166 #endif
167
168 if (strcmp(stop_mode, "") != 0)
169 {
170 snprintf(command_text, sizeof(command_text),
171 "%s %s -D %s -m %s 2>/dev/null 1>/dev/null < /dev/null &",
172 pg_ctl, action, data_directory, stop_mode);
173
174 } else {
175 snprintf(command_text, sizeof(command_text),
176 "%s %s -D %s 2>/dev/null 1>/dev/null < /dev/null &",
177 pg_ctl, action, data_directory);
178 }
179
180 elog(DEBUG1, "command_text: %s", command_text);
181 r = system(command_text);
182
183 if (strcmp(action, "reload") == 0 && r != 0)
184 {
185 elog(ERROR, "pgpool_pgctl failed");
186 }
187
188 PG_RETURN_BOOL(true);
189 }
190
191 /*
192 * pgpool_switch_log is the same as pg_switch_xlog except that
193 * it wait till archiving is completed.
194 * We call xlog functions with the oid to avoid a compile error
195 * at old PostgreSQL.
196 */
197 Datum
pgpool_switch_xlog(PG_FUNCTION_ARGS)198 pgpool_switch_xlog(PG_FUNCTION_ARGS)
199 {
200 char *archive_dir;
201 char *filename;
202 char path[MAXPGPATH];
203 struct stat fst;
204 Datum location;
205 text *filename_t;
206 text *result;
207 Oid switch_xlog_oid;
208 Oid xlogfile_name_oid;
209
210 #if !defined(PG_VERSION_NUM) || (PG_VERSION_NUM < 90400)
211 char* pg_xlogfile_name_arg_type = "text";
212 #else
213 /*
214 * The argument data type of PG's pg_xlogfile_name() function
215 * has been changed from text to pg_lsn since PostgreSQL 9.4
216 */
217 char* pg_xlogfile_name_arg_type = "pg_lsn";
218 #endif
219
220
221 archive_dir = DatumGetCString(DirectFunctionCall1(textout,
222 PointerGetDatum(PG_GETARG_TEXT_P(0))));
223
224 if (stat(archive_dir, &fst) < 0)
225 #ifdef ERRCODE_INSUFFICIENT_PRIVILEGE
226 ereport(ERROR,
227 (errcode_for_file_access(),
228 errmsg("could not stat file \"%s\": %m", archive_dir)));
229 #else
230 elog(ERROR, "could not stat file \"%s\"", archive_dir);
231 #endif
232
233 switch_xlog_oid = get_function_oid("pg_switch_xlog", NULL, "pg_catalog");
234 xlogfile_name_oid = get_function_oid("pg_xlogfile_name", pg_xlogfile_name_arg_type, "pg_catalog");
235
236 if (!switch_xlog_oid || !xlogfile_name_oid)
237 {
238 /* probably PostgreSQL is 10 or greater */
239 switch_xlog_oid = get_function_oid("pg_switch_wal", NULL, "pg_catalog");
240 xlogfile_name_oid = get_function_oid("pg_walfile_name", pg_xlogfile_name_arg_type, "pg_catalog");
241
242 if (!switch_xlog_oid || !xlogfile_name_oid)
243 elog(ERROR, "cannot find xlog functions");
244 }
245
246 location = OidFunctionCall1(switch_xlog_oid, PointerGetDatum(NULL));
247 filename_t = DatumGetTextP(OidFunctionCall1(xlogfile_name_oid, location));
248 filename = DatumGetCString(DirectFunctionCall1(textout,
249 PointerGetDatum(filename_t)));
250
251 snprintf(path, MAXPGPATH, "%s/%s", archive_dir, filename);
252 elog(LOG, "pgpool_switch_xlog: waiting for \"%s\"", path);
253
254 while(stat(path, &fst) != 0 || fst.st_size == 0 ||
255 fst.st_size % (1024 * 1024) != 0)
256 {
257 CHECK_FOR_INTERRUPTS();
258 sleep(1);
259 }
260
261 result = DatumGetTextP(DirectFunctionCall1(textin,
262 CStringGetDatum(path)));
263
264 PG_RETURN_TEXT_P(result);
265 }
266
267 static Oid
get_function_oid(const char * funcname,const char * argtype,const char * nspname)268 get_function_oid(const char *funcname, const char *argtype, const char *nspname)
269 {
270 #ifndef PROCNAMENSP
271 Oid typid;
272 Oid nspid;
273 Oid funcid;
274 Oid oids[1];
275 oidvector *oid_v;
276 HeapTuple tup;
277
278 if (argtype)
279 {
280 typid = TypenameGetTypid(argtype);
281 elog(DEBUG1, "get_function_oid: %s typid: %d", argtype, typid);
282 oids[0] = typid;
283 oid_v = buildoidvector(oids, 1);
284 }
285 else
286 {
287 oid_v = buildoidvector(NULL, 0);
288 }
289
290 #if !defined(PG_VERSION_NUM) || (PG_VERSION_NUM < 90300)
291 nspid = LookupExplicitNamespace(nspname);
292 #else
293 /* LookupExplicitNamespace() of PostgreSQL 9.3 or later, has third
294 * argument "missing_ok" which suppresses ERROR exception, but
295 * returns invlaid_oid. See include/catalog/namespace.h */
296 nspid = LookupExplicitNamespace(nspname, false);
297 #endif
298
299 elog(DEBUG1, "get_function_oid: oid of \"%s\": %d", nspname, nspid);
300
301
302 tup = SearchSysCache(PROCNAMEARGSNSP,
303 PointerGetDatum(funcname),
304 PointerGetDatum(oid_v),
305 ObjectIdGetDatum(nspid),
306 0);
307
308 if (HeapTupleIsValid(tup))
309 {
310 #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 120000)
311 Form_pg_proc proctup = (Form_pg_proc) GETSTRUCT(tup);
312 funcid = proctup->oid;
313 #else
314 funcid = HeapTupleGetOid(tup);
315 #endif
316 elog(DEBUG1, "get_function_oid: oid of \"%s\": %d", funcname, funcid);
317 ReleaseSysCache(tup);
318 return funcid;
319 }
320 #endif
321 return 0;
322 }
323
324