summaryrefslogtreecommitdiff
path: root/contrib/pg_upgrade/exec.c
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>2010-05-12 02:19:11 +0000
committerBruce Momjian <bruce@momjian.us>2010-05-12 02:19:11 +0000
commitc2e9b2f288185a8569f6391ea250c7eeafa6c14b (patch)
tree408e8eb0c0aacaf177602789c02d7a416bbd59e1 /contrib/pg_upgrade/exec.c
parent28e1742217716076da0700094a369eae5766974c (diff)
Add pg_upgrade to /contrib; will be in 9.0 beta2.
Add documentation. Supports migration from PG 8.3 and 8.4.
Diffstat (limited to 'contrib/pg_upgrade/exec.c')
-rw-r--r--contrib/pg_upgrade/exec.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/contrib/pg_upgrade/exec.c b/contrib/pg_upgrade/exec.c
new file mode 100644
index 00000000000..8765d7e41bc
--- /dev/null
+++ b/contrib/pg_upgrade/exec.c
@@ -0,0 +1,338 @@
+/*
+ * exec.c
+ *
+ * execution functions
+ */
+
+#include "pg_upgrade.h"
+
+#include <fcntl.h>
+#include <grp.h>
+
+
+static void checkBinDir(migratorContext *ctx, ClusterInfo *cluster);
+static int check_exec(migratorContext *ctx, const char *dir, const char *cmdName,
+ const char *alternative);
+static const char *validate_exec(const char *path);
+static int check_data_dir(migratorContext *ctx, const char *pg_data);
+
+
+/*
+ * exec_prog()
+ *
+ * Formats a command from the given argument list and executes that
+ * command. If the command executes, exec_prog() returns 1 otherwise
+ * exec_prog() logs an error message and returns 0.
+ *
+ * If throw_error is TRUE, this function will throw a PG_FATAL error
+ * instead of returning should an error occur.
+ */
+int
+exec_prog(migratorContext *ctx, bool throw_error, const char *fmt,...)
+{
+ va_list args;
+ int result;
+ char cmd[MAXPGPATH];
+
+ va_start(args, fmt);
+ vsnprintf(cmd, MAXPGPATH, fmt, args);
+ va_end(args);
+
+ pg_log(ctx, PG_INFO, "%s\n", cmd);
+
+ result = system(cmd);
+
+ if (result != 0)
+ {
+ pg_log(ctx, throw_error ? PG_FATAL : PG_INFO,
+ "\nThere were problems executing %s\n", cmd);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * verify_directories()
+ *
+ * does all the hectic work of verifying directories and executables
+ * of old and new server.
+ *
+ * NOTE: May update the values of all parameters
+ */
+void
+verify_directories(migratorContext *ctx)
+{
+ prep_status(ctx, "Checking old data directory (%s)", ctx->old.pgdata);
+ if (check_data_dir(ctx, ctx->old.pgdata) != 0)
+ pg_log(ctx, PG_FATAL, "Failed\n");
+ checkBinDir(ctx, &ctx->old);
+ check_ok(ctx);
+
+ prep_status(ctx, "Checking new data directory (%s)", ctx->new.pgdata);
+ if (check_data_dir(ctx, ctx->new.pgdata) != 0)
+ pg_log(ctx, PG_FATAL, "Failed\n");
+ checkBinDir(ctx, &ctx->new);
+ check_ok(ctx);
+}
+
+
+/*
+ * checkBinDir()
+ *
+ * This function searches for the executables that we expect to find
+ * in the binaries directory. If we find that a required executable
+ * is missing (or secured against us), we display an error message and
+ * exit().
+ */
+static void
+checkBinDir(migratorContext *ctx, ClusterInfo *cluster)
+{
+ check_exec(ctx, cluster->bindir, "postgres", "edb-postgres");
+ check_exec(ctx, cluster->bindir, "pg_ctl", NULL);
+ check_exec(ctx, cluster->bindir, "pg_dumpall", NULL);
+
+#ifdef EDB_NATIVE_LANG
+ /* check for edb-psql first because we need to detect EDB AS */
+ if (check_exec(ctx, cluster->bindir, "edb-psql", "psql") == 1)
+ {
+ cluster->psql_exe = "edb-psql";
+ cluster->is_edb_as = true;
+ }
+ else
+#else
+ if (check_exec(ctx, cluster->bindir, "psql", NULL) == 1)
+#endif
+ cluster->psql_exe = "psql";
+}
+
+
+/*
+ * is_server_running()
+ *
+ * checks whether postmaster on the given data directory is running or not.
+ * The check is performed by looking for the existence of postmaster.pid file.
+ */
+bool
+is_server_running(migratorContext *ctx, const char *datadir)
+{
+ char path[MAXPGPATH];
+ int fd;
+
+ snprintf(path, sizeof(path), "%s/postmaster.pid", datadir);
+
+ if ((fd = open(path, O_RDONLY)) < 0)
+ {
+ if (errno != ENOENT)
+ pg_log(ctx, PG_FATAL, "\ncould not open file \"%s\" for reading\n",
+ path);
+
+ return false;
+ }
+
+ close(fd);
+ return true;
+}
+
+
+/*
+ * check_exec()
+ *
+ * Checks whether either of the two command names (cmdName and alternative)
+ * appears to be an executable (in the given directory). If dir/cmdName is
+ * an executable, this function returns 1. If dir/alternative is an
+ * executable, this function returns 2. If neither of the given names is
+ * a valid executable, this function returns 0 to indicated failure.
+ */
+static int
+check_exec(migratorContext *ctx, const char *dir, const char *cmdName,
+ const char *alternative)
+{
+ char path[MAXPGPATH];
+ const char *errMsg;
+
+ snprintf(path, sizeof(path), "%s%c%s", dir, pathSeparator, cmdName);
+
+ if ((errMsg = validate_exec(path)) == NULL)
+ {
+ return 1; /* 1 -> first alternative OK */
+ }
+ else
+ {
+ if (alternative)
+ {
+ report_status(ctx, PG_WARNING, "check for %s warning: %s",
+ cmdName, errMsg);
+ if (check_exec(ctx, dir, alternative, NULL) == 1)
+ return 2; /* 2 -> second alternative OK */
+ }
+ else
+ pg_log(ctx, PG_FATAL, "check for %s failed - %s\n", cmdName, errMsg);
+ }
+
+ return 0; /* 0 -> neither alternative is acceptable */
+}
+
+
+/*
+ * 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 const char *
+validate_exec(const char *path)
+{
+ struct stat buf;
+
+#ifndef WIN32
+ uid_t euid;
+ struct group *gp;
+ struct passwd *pwp;
+ int in_grp = 0;
+#else
+ char path_exe[MAXPGPATH + sizeof(EXE_EXT) - 1];
+#endif
+
+#ifdef WIN32
+ /* Win32 requires a .exe suffix for stat() */
+
+ if (strlen(path) >= strlen(EXE_EXT) &&
+ pg_strcasecmp(path + strlen(path) - strlen(EXE_EXT), EXE_EXT) != 0)
+ {
+ strcpy(path_exe, path);
+ strcat(path_exe, EXE_EXT);
+ path = path_exe;
+ }
+#endif
+
+ /*
+ * Ensure that the file exists and is a regular file.
+ */
+ if (stat(path, &buf) < 0)
+ return getErrorText(errno);
+
+ if ((buf.st_mode & S_IFMT) != S_IFREG)
+ return "not an executable file";
+
+ /*
+ * Ensure that we are using an authorized executable.
+ */
+
+ /*
+ * Ensure that the file is both executable and readable (required for
+ * dynamic loading).
+ */
+#ifndef WIN32
+ euid = geteuid();
+
+ /* If owned by us, just check owner bits */
+ if (euid == buf.st_uid)
+ {
+ if ((buf.st_mode & S_IRUSR) == 0)
+ return "can't read file (permission denied)";
+ if ((buf.st_mode & S_IXUSR) == 0)
+ return "can't execute (permission denied)";
+ return NULL;
+ }
+
+ /* OK, check group bits */
+ pwp = getpwuid(euid); /* not thread-safe */
+
+ if (pwp)
+ {
+ if (pwp->pw_gid == buf.st_gid) /* my primary group? */
+ ++in_grp;
+ else if (pwp->pw_name &&
+ (gp = getgrgid(buf.st_gid)) != NULL &&
+ /* not thread-safe */ gp->gr_mem != NULL)
+ {
+ /* try list of member groups */
+ int i;
+
+ for (i = 0; gp->gr_mem[i]; ++i)
+ {
+ if (!strcmp(gp->gr_mem[i], pwp->pw_name))
+ {
+ ++in_grp;
+ break;
+ }
+ }
+ }
+
+ if (in_grp)
+ {
+ if ((buf.st_mode & S_IRGRP) == 0)
+ return "can't read file (permission denied)";
+ if ((buf.st_mode & S_IXGRP) == 0)
+ return "can't execute (permission denied)";
+ return NULL;
+ }
+ }
+
+ /* Check "other" bits */
+ if ((buf.st_mode & S_IROTH) == 0)
+ return "can't read file (permission denied)";
+ if ((buf.st_mode & S_IXOTH) == 0)
+ return "can't execute (permission denied)";
+ return NULL;
+#else
+ if ((buf.st_mode & S_IRUSR) == 0)
+ return "can't read file (permission denied)";
+ if ((buf.st_mode & S_IXUSR) == 0)
+ return "can't execute (permission denied)";
+ return NULL;
+#endif
+}
+
+
+/*
+ * check_data_dir()
+ *
+ * This function validates the given cluster directory - we search for a
+ * small set of subdirectories that we expect to find in a valid $PGDATA
+ * directory. If any of the subdirectories are missing (or secured against
+ * us) we display an error message and exit()
+ *
+ */
+static int
+check_data_dir(migratorContext *ctx, const char *pg_data)
+{
+ char subDirName[MAXPGPATH];
+ const char *requiredSubdirs[] = {"base", "global", "pg_clog",
+ "pg_multixact", "pg_subtrans",
+ "pg_tblspc", "pg_twophase", "pg_xlog"};
+ bool fail = false;
+ int subdirnum;
+
+ for (subdirnum = 0; subdirnum < sizeof(requiredSubdirs) / sizeof(requiredSubdirs[0]); ++subdirnum)
+ {
+ struct stat statBuf;
+
+ snprintf(subDirName, sizeof(subDirName), "%s%c%s", pg_data,
+ pathSeparator, requiredSubdirs[subdirnum]);
+
+ if ((stat(subDirName, &statBuf)) != 0)
+ {
+ report_status(ctx, PG_WARNING, "check for %s warning: %s",
+ requiredSubdirs[subdirnum], getErrorText(errno));
+ fail = true;
+ }
+ else
+ {
+ if (!S_ISDIR(statBuf.st_mode))
+ {
+ report_status(ctx, PG_WARNING, "%s is not a directory",
+ requiredSubdirs[subdirnum]);
+ fail = true;
+ }
+ }
+ }
+
+ return (fail) ? -1 : 0;
+}
+
+