{
int ret;
PQExpBuffer cmd = createPQExpBuffer();
- char *exec_path = find_other_exec_or_die(argv0, "pg_ctl", "pg_ctl (PostgreSQL) " PG_VERSION "\n");
+ char *exec_path = find_other_exec_or_die(argv0, "pg_ctl", NULL);
appendPQExpBuffer(cmd, "%s %s -D \"%s\" -s", exec_path, arg, data_dir);
{
int ret;
PQExpBuffer cmd = createPQExpBuffer();
- char *exec_path = find_other_exec_or_die(argv0, "pg_basebackup", "pg_basebackup (PostgreSQL) " PG_VERSION "\n");
+ char *exec_path = find_other_exec_or_die(argv0, "pg_basebackup", NULL);
appendPQExpBuffer(cmd, "%s -D \"%s\" -d \"%s\" -X s -P", exec_path, data_dir, remote_connstr);
free(buffer);
}
-/*
- * Utility functions taken from pg_ctl
- */
static char *
find_other_exec_or_die(const char *argv0, const char *target, const char *versionstr)
{
int ret;
char *found_path;
+ uint32 bin_version;
found_path = pg_malloc(MAXPGPATH);
- if ((ret = find_other_exec(argv0, target, versionstr, found_path)) < 0)
+ if (versionstr)
+ ret = find_other_exec(argv0, target, versionstr, found_path);
+ else
+ ret = bdr_find_other_exec(argv0, target, &bin_version, found_path);
+
+ if (ret < 0)
{
char full_path[MAXPGPATH];
"Check your installation.\n"),
target, full_path, progname);
}
+ else if (!versionstr)
+ {
+ char full_path[MAXPGPATH];
+
+ if (find_my_exec(argv0, full_path) < 0)
+ strlcpy(full_path, progname, sizeof(full_path));
+
+ if (bin_version / 100 != PG_VERSION_NUM / 100)
+ die(_("The program \"%s\" was found by \"%s\"\n"
+ "but was not the same version as %s.\n"
+ "Check your installation.\n"),
+ target, full_path, progname);
+
+ }
return found_path;
}
StringInfoData origin_dsn;
StringInfoData local_dsn;
int saved_errno;
+ uint32 bin_version;
initStringInfo(&path);
initStringInfo(&origin_dsn);
BDR_INIT_REPLICA_CMD " (PostgreSQL " PG_VERSION ", BDR " BDR_VERSION ")\n",
&bdr_init_replica_script_path[0]) < 0)
{
- elog(ERROR, "bdr: failed to find " BDR_INIT_REPLICA_CMD
+ elog(ERROR, "bdr node init failed to find " BDR_INIT_REPLICA_CMD
" relative to binary %s or wrong version. Expected (PostgreSQL %s, BDR %s)",
my_exec_path, PG_VERSION, BDR_VERSION);
}
- if (find_other_exec(my_exec_path, BDR_DUMP_CMD,
- "pg_dump (PostgreSQL) " PG_VERSION "\n",
+ if (bdr_find_other_exec(my_exec_path, BDR_DUMP_CMD,
+ &bin_version,
&bdr_dump_path[0]) < 0)
{
- elog(ERROR, "bdr: failed to find " BDR_DUMP_CMD
- " relative to binary %s or wrong version (expected %s)",
- my_exec_path, PG_VERSION);
+ elog(ERROR, "bdr node init failed to find " BDR_DUMP_CMD
+ " relative to binary %s",
+ my_exec_path);
+ }
+ if (bin_version / 100 != PG_VERSION_NUM / 100)
+ {
+ elog(ERROR, "bdr node init found " BDR_DUMP_CMD
+ " with wrong major version %d.%d, expected %d.%d",
+ bin_version / 100 / 100, bin_version / 100 % 100,
+ PG_VERSION_NUM / 100 / 100, PG_VERSION_NUM / 100 % 100);
}
- if (find_other_exec(my_exec_path, BDR_RESTORE_CMD,
- BDR_RESTORE_CMD " (PostgreSQL) " PG_VERSION "\n",
+ if (bdr_find_other_exec(my_exec_path, BDR_RESTORE_CMD,
+ &bin_version,
&bdr_restore_path[0]) < 0)
{
- elog(ERROR, "bdr: failed to find " BDR_RESTORE_CMD
- " relative to binary %s or wrong version (expected %s)",
- my_exec_path, PG_VERSION);
+ elog(ERROR, "bdr node init failed to find " BDR_RESTORE_CMD
+ " relative to binary %s",
+ my_exec_path);
+ }
+ if (bin_version / 100 != PG_VERSION_NUM / 100)
+ {
+ elog(ERROR, "bdr node init found " BDR_RESTORE_CMD
+ " with wrong major version %d.%d, expected %d.%d",
+ bin_version / 100 / 100, bin_version / 100 % 100,
+ PG_VERSION_NUM / 100 / 100, PG_VERSION_NUM / 100 % 100);
}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * bdr_pgutils.c
+ * This files is used as common place for function that we took
+ * verbatim from PostgreSQL because they are declared as static and
+ * we can't use them as API.
+ * Also the internal API function that use those static functions
+ * be defined here and should start with bdr_ prefix.
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * bdr_pgutils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "access/xlogdefs.h"
+#include "nodes/pg_list.h"
+
+#include "bdr_internal.h"
+
+#ifndef FRONTEND
+#define log_error4(str, param, arg1) elog(LOG, str, param, arg1)
+#else
+#define log_error4(str, param, arg1) (fprintf(stderr, str, param, arg1), fputc('\n', stderr))
+#endif
+
+/*
+ * Start function taken from src/common/exec.c
+ */
+
+static int validate_exec(const char *path);
+static char *pipe_read_line(char *cmd, char *line, int maxsize);
+
+/*
+ * validate_exec -- validate "path" as an executable file
+ *
+ * returns 0 if the file is found and no error is encountered.
+ * -1 if the regular file "path" does not exist or cannot be executed.
+ * -2 if the file is otherwise valid but cannot be read.
+ */
+static int
+validate_exec(const char *path)
+{
+ struct stat buf;
+ int is_r;
+ int is_x;
+
+#ifdef WIN32
+ char path_exe[MAXPGPATH + sizeof(".exe") - 1];
+
+ /* Win32 requires a .exe suffix for stat() */
+ if (strlen(path) >= strlen(".exe") &&
+ pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
+ {
+ strlcpy(path_exe, path, sizeof(path_exe) - 4);
+ strcat(path_exe, ".exe");
+ path = path_exe;
+ }
+#endif
+
+ /*
+ * Ensure that the file exists and is a regular file.
+ *
+ * XXX if you have a broken system where stat() looks at the symlink
+ * instead of the underlying file, you lose.
+ */
+ if (stat(path, &buf) < 0)
+ return -1;
+
+ if (!S_ISREG(buf.st_mode))
+ return -1;
+
+ /*
+ * Ensure that the file is both executable and readable (required for
+ * dynamic loading).
+ */
+#ifndef WIN32
+ is_r = (access(path, R_OK) == 0);
+ is_x = (access(path, X_OK) == 0);
+#else
+ is_r = buf.st_mode & S_IRUSR;
+ is_x = buf.st_mode & S_IXUSR;
+#endif
+ return is_x ? (is_r ? 0 : -2) : -1;
+}
+
+
+/*
+ * Find another program in our binary's directory,
+ * then make sure it is the proper version.
+ *
+ * BDR modified version - returns computed major version number
+ */
+int
+bdr_find_other_exec(const char *argv0, const char *target,
+ uint32 *version, char *retpath)
+{
+ char cmd[MAXPGPATH];
+ char line[100];
+ int pre_dot,
+ post_dot;
+
+ if (find_my_exec(argv0, retpath) < 0)
+ return -1;
+
+ /* Trim off program name and keep just directory */
+ *last_dir_separator(retpath) = '\0';
+ canonicalize_path(retpath);
+
+ /* Now append the other program's name */
+ snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
+ "/%s%s", target, EXE);
+
+ if (validate_exec(retpath) != 0)
+ return -1;
+
+ snprintf(cmd, sizeof(cmd), "\"%s\" -V", retpath);
+
+ if (!pipe_read_line(cmd, line, sizeof(line)))
+ return -1;
+
+ if (sscanf(line, "%*s %*s %d.%d", &pre_dot, &post_dot) != 2)
+ return -2;
+
+ *version = (pre_dot * 100 + post_dot) * 100;
+
+ return 0;
+}
+
+
+/*
+ * The runtime library's popen() on win32 does not work when being
+ * called from a service when running on windows <= 2000, because
+ * there is no stdin/stdout/stderr.
+ *
+ * Executing a command in a pipe and reading the first line from it
+ * is all we need.
+ */
+static char *
+pipe_read_line(char *cmd, char *line, int maxsize)
+{
+#ifndef WIN32
+ FILE *pgver;
+
+ /* flush output buffers in case popen does not... */
+ fflush(stdout);
+ fflush(stderr);
+
+ errno = 0;
+ if ((pgver = popen(cmd, "r")) == NULL)
+ {
+ perror("popen failure");
+ return NULL;
+ }
+
+ errno = 0;
+ if (fgets(line, maxsize, pgver) == NULL)
+ {
+ if (feof(pgver))
+ fprintf(stderr, "no data was returned by command \"%s\"\n", cmd);
+ else
+ perror("fgets failure");
+ pclose(pgver); /* no error checking */
+ return NULL;
+ }
+
+ if (pclose_check(pgver))
+ return NULL;
+
+ return line;
+#else /* WIN32 */
+
+ SECURITY_ATTRIBUTES sattr;
+ HANDLE childstdoutrd,
+ childstdoutwr,
+ childstdoutrddup;
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si;
+ char *retval = NULL;
+
+ sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sattr.bInheritHandle = TRUE;
+ sattr.lpSecurityDescriptor = NULL;
+
+ if (!CreatePipe(&childstdoutrd, &childstdoutwr, &sattr, 0))
+ return NULL;
+
+ if (!DuplicateHandle(GetCurrentProcess(),
+ childstdoutrd,
+ GetCurrentProcess(),
+ &childstdoutrddup,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ CloseHandle(childstdoutrd);
+ CloseHandle(childstdoutwr);
+ return NULL;
+ }
+
+ CloseHandle(childstdoutrd);
+
+ ZeroMemory(&pi, sizeof(pi));
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdError = childstdoutwr;
+ si.hStdOutput = childstdoutwr;
+ si.hStdInput = INVALID_HANDLE_VALUE;
+
+ if (CreateProcess(NULL,
+ cmd,
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &si,
+ &pi))
+ {
+ /* Successfully started the process */
+ char *lineptr;
+
+ ZeroMemory(line, maxsize);
+
+ /* Try to read at least one line from the pipe */
+ /* This may require more than one wait/read attempt */
+ for (lineptr = line; lineptr < line + maxsize - 1;)
+ {
+ DWORD bytesread = 0;
+
+ /* Let's see if we can read */
+ if (WaitForSingleObject(childstdoutrddup, 10000) != WAIT_OBJECT_0)
+ break; /* Timeout, but perhaps we got a line already */
+
+ if (!ReadFile(childstdoutrddup, lineptr, maxsize - (lineptr - line),
+ &bytesread, NULL))
+ break; /* Error, but perhaps we got a line already */
+
+ lineptr += strlen(lineptr);
+
+ if (!bytesread)
+ break; /* EOF */
+
+ if (strchr(line, '\n'))
+ break; /* One or more lines read */
+ }
+
+ if (lineptr != line)
+ {
+ /* OK, we read some data */
+ int len;
+
+ /* If we got more than one line, cut off after the first \n */
+ lineptr = strchr(line, '\n');
+ if (lineptr)
+ *(lineptr + 1) = '\0';
+
+ len = strlen(line);
+
+ /*
+ * If EOL is \r\n, convert to just \n. Because stdout is a
+ * text-mode stream, the \n output by the child process is
+ * received as \r\n, so we convert it to \n. The server main.c
+ * sets setvbuf(stdout, NULL, _IONBF, 0) which has the effect of
+ * disabling \n to \r\n expansion for stdout.
+ */
+ if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n')
+ {
+ line[len - 2] = '\n';
+ line[len - 1] = '\0';
+ len--;
+ }
+
+ /*
+ * We emulate fgets() behaviour. So if there is no newline at the
+ * end, we add one...
+ */
+ if (len == 0 || line[len - 1] != '\n')
+ strcat(line, "\n");
+
+ retval = line;
+ }
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+
+ CloseHandle(childstdoutwr);
+ CloseHandle(childstdoutrddup);
+
+ return retval;
+#endif /* WIN32 */
+}
+
+/*
+ * End function taken from src/common/exec.c
+ */