summaryrefslogtreecommitdiffstats
path: root/src/bin/pg_upgrade/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/pg_upgrade/util.c')
-rw-r--r--src/bin/pg_upgrade/util.c355
1 files changed, 355 insertions, 0 deletions
diff --git a/src/bin/pg_upgrade/util.c b/src/bin/pg_upgrade/util.c
new file mode 100644
index 0000000..21ba4c8
--- /dev/null
+++ b/src/bin/pg_upgrade/util.c
@@ -0,0 +1,355 @@
+/*
+ * util.c
+ *
+ * utility functions
+ *
+ * Copyright (c) 2010-2023, PostgreSQL Global Development Group
+ * src/bin/pg_upgrade/util.c
+ */
+
+#include "postgres_fe.h"
+
+#include <signal.h>
+
+#include "common/username.h"
+#include "pg_upgrade.h"
+
+LogOpts log_opts;
+
+static void pg_log_v(eLogType type, const char *fmt, va_list ap) pg_attribute_printf(2, 0);
+
+
+/*
+ * report_status()
+ *
+ * Displays the result of an operation (ok, failed, error message,...)
+ *
+ * This is no longer functionally different from pg_log(), but we keep
+ * it around to maintain a notational distinction between operation
+ * results and other messages.
+ */
+void
+report_status(eLogType type, const char *fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ pg_log_v(type, fmt, args);
+ va_end(args);
+}
+
+
+void
+end_progress_output(void)
+{
+ /*
+ * For output to a tty, erase prior contents of progress line. When either
+ * tty or verbose, indent so that report_status() output will align
+ * nicely.
+ */
+ if (log_opts.isatty)
+ {
+ printf("\r");
+ pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, "");
+ }
+ else if (log_opts.verbose)
+ pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, "");
+}
+
+/*
+ * Remove any logs generated internally. To be used once when exiting.
+ */
+void
+cleanup_output_dirs(void)
+{
+ fclose(log_opts.internal);
+
+ /* Remove dump and log files? */
+ if (log_opts.retain)
+ return;
+
+ /*
+ * Try twice. The second time might wait for files to finish being
+ * unlinked, on Windows.
+ */
+ if (!rmtree(log_opts.basedir, true))
+ rmtree(log_opts.basedir, true);
+
+ /* Remove pg_upgrade_output.d only if empty */
+ switch (pg_check_dir(log_opts.rootdir))
+ {
+ case 0: /* non-existent */
+ case 3: /* exists and contains a mount point */
+ Assert(false);
+ break;
+
+ case 1: /* exists and empty */
+ case 2: /* exists and contains only dot files */
+
+ /*
+ * Try twice. The second time might wait for files to finish
+ * being unlinked, on Windows.
+ */
+ if (!rmtree(log_opts.rootdir, true))
+ rmtree(log_opts.rootdir, true);
+ break;
+
+ case 4: /* exists */
+
+ /*
+ * Keep the root directory as this includes some past log
+ * activity.
+ */
+ break;
+
+ default:
+ /* different failure, just report it */
+ pg_log(PG_WARNING, "could not access directory \"%s\": %m",
+ log_opts.rootdir);
+ break;
+ }
+}
+
+/*
+ * prep_status
+ *
+ * Displays a message that describes an operation we are about to begin.
+ * We pad the message out to MESSAGE_WIDTH characters so that all of the
+ * "ok" and "failed" indicators line up nicely. (Overlength messages
+ * will be truncated, so don't get too verbose.)
+ *
+ * A typical sequence would look like this:
+ * prep_status("about to flarb the next %d files", fileCount);
+ * if ((message = flarbFiles(fileCount)) == NULL)
+ * report_status(PG_REPORT, "ok");
+ * else
+ * pg_log(PG_FATAL, "failed: %s", message);
+ */
+void
+prep_status(const char *fmt,...)
+{
+ va_list args;
+ char message[MAX_STRING];
+
+ va_start(args, fmt);
+ vsnprintf(message, sizeof(message), fmt, args);
+ va_end(args);
+
+ /* trim strings */
+ pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, message);
+}
+
+/*
+ * prep_status_progress
+ *
+ * Like prep_status(), but for potentially longer running operations.
+ * Details about what item is currently being processed can be displayed
+ * with pg_log(PG_STATUS, ...). A typical sequence would look like this:
+ *
+ * prep_status_progress("copying files");
+ * for (...)
+ * pg_log(PG_STATUS, "%s", filename);
+ * end_progress_output();
+ * report_status(PG_REPORT, "ok");
+ */
+void
+prep_status_progress(const char *fmt,...)
+{
+ va_list args;
+ char message[MAX_STRING];
+
+ va_start(args, fmt);
+ vsnprintf(message, sizeof(message), fmt, args);
+ va_end(args);
+
+ /*
+ * If outputting to a tty or in verbose, append newline. pg_log_v() will
+ * put the individual progress items onto the next line.
+ */
+ if (log_opts.isatty || log_opts.verbose)
+ pg_log(PG_REPORT, "%-*s", MESSAGE_WIDTH, message);
+ else
+ pg_log(PG_REPORT_NONL, "%-*s", MESSAGE_WIDTH, message);
+}
+
+static void
+pg_log_v(eLogType type, const char *fmt, va_list ap)
+{
+ char message[QUERY_ALLOC];
+
+ /* No incoming message should end in newline; we add that here. */
+ Assert(fmt);
+ Assert(fmt[0] == '\0' || fmt[strlen(fmt) - 1] != '\n');
+
+ vsnprintf(message, sizeof(message), _(fmt), ap);
+
+ /* PG_VERBOSE and PG_STATUS are only output in verbose mode */
+ /* fopen() on log_opts.internal might have failed, so check it */
+ if (((type != PG_VERBOSE && type != PG_STATUS) || log_opts.verbose) &&
+ log_opts.internal != NULL)
+ {
+ if (type == PG_STATUS)
+ /* status messages get two leading spaces, see below */
+ fprintf(log_opts.internal, " %s\n", message);
+ else if (type == PG_REPORT_NONL)
+ fprintf(log_opts.internal, "%s", message);
+ else
+ fprintf(log_opts.internal, "%s\n", message);
+ fflush(log_opts.internal);
+ }
+
+ switch (type)
+ {
+ case PG_VERBOSE:
+ if (log_opts.verbose)
+ printf("%s\n", message);
+ break;
+
+ case PG_STATUS:
+
+ /*
+ * For output to a terminal, we add two leading spaces and no
+ * newline; instead append \r so that the next message is output
+ * on the same line. Truncate on the left to fit into
+ * MESSAGE_WIDTH (counting the spaces as part of that).
+ *
+ * If going to non-interactive output, only display progress if
+ * verbose is enabled. Otherwise the output gets unreasonably
+ * large by default.
+ */
+ if (log_opts.isatty)
+ {
+ bool itfits = (strlen(message) <= MESSAGE_WIDTH - 2);
+
+ /* prefix with "..." if we do leading truncation */
+ printf(" %s%-*.*s\r",
+ itfits ? "" : "...",
+ MESSAGE_WIDTH - 2, MESSAGE_WIDTH - 2,
+ itfits ? message :
+ message + strlen(message) - MESSAGE_WIDTH + 3 + 2);
+ }
+ else if (log_opts.verbose)
+ printf(" %s\n", message);
+ break;
+
+ case PG_REPORT_NONL:
+ /* This option is for use by prep_status and friends */
+ printf("%s", message);
+ break;
+
+ case PG_REPORT:
+ case PG_WARNING:
+ printf("%s\n", message);
+ break;
+
+ case PG_FATAL:
+ /* Extra newline in case we're interrupting status output */
+ printf("\n%s\n", message);
+ printf(_("Failure, exiting\n"));
+ exit(1);
+ break;
+
+ /* No default:, we want a warning for omitted cases */
+ }
+ fflush(stdout);
+}
+
+
+void
+pg_log(eLogType type, const char *fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ pg_log_v(type, fmt, args);
+ va_end(args);
+}
+
+
+void
+pg_fatal(const char *fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ pg_log_v(PG_FATAL, fmt, args);
+ va_end(args);
+ /* NOTREACHED */
+ printf(_("Failure, exiting\n"));
+ exit(1);
+}
+
+
+void
+check_ok(void)
+{
+ /* all seems well */
+ report_status(PG_REPORT, "ok");
+}
+
+
+/*
+ * quote_identifier()
+ * Properly double-quote a SQL identifier.
+ *
+ * The result should be pg_free'd, but most callers don't bother because
+ * memory leakage is not a big deal in this program.
+ */
+char *
+quote_identifier(const char *s)
+{
+ char *result = pg_malloc(strlen(s) * 2 + 3);
+ char *r = result;
+
+ *r++ = '"';
+ while (*s)
+ {
+ if (*s == '"')
+ *r++ = *s;
+ *r++ = *s;
+ s++;
+ }
+ *r++ = '"';
+ *r++ = '\0';
+
+ return result;
+}
+
+
+/*
+ * get_user_info()
+ */
+int
+get_user_info(char **user_name_p)
+{
+ int user_id;
+ const char *user_name;
+ char *errstr;
+
+#ifndef WIN32
+ user_id = geteuid();
+#else
+ user_id = 1;
+#endif
+
+ user_name = get_user_name(&errstr);
+ if (!user_name)
+ pg_fatal("%s", errstr);
+
+ /* make a copy */
+ *user_name_p = pg_strdup(user_name);
+
+ return user_id;
+}
+
+
+/*
+ * str2uint()
+ *
+ * convert string to oid
+ */
+unsigned int
+str2uint(const char *str)
+{
+ return strtoul(str, NULL, 10);
+}