]> granicus.if.org Git - postgresql/commitdiff
Preliminary code review for C version of initdb. Re-centralize handling
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 13 Nov 2003 23:46:31 +0000 (23:46 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 13 Nov 2003 23:46:31 +0000 (23:46 +0000)
of option switches for backend, fix handling of COPY from data files so
that we won't have the newline-after-\. issue back again, add back some
comments and printouts lost from the shell script, etc.  Still needs work
for error handling; in particular the shell version worked much more
nicely for the case of a postgres executable that fails on invocation.

src/bin/initdb/initdb.c

index 46d64e08d499376a9eefd2584c8b0bf7afd8c316..43f8304e904529ba83e840916247690fe85c0be7 100644 (file)
@@ -1,29 +1,48 @@
 /*-------------------------------------------------------------------------
  *
- * initdb
+ * initdb --- initialize a PostgreSQL installation
  *
- * author: Andrew Dunstan         mailto:andrew@dunslane.net
+ * initdb creates (initializes) a PostgreSQL database cluster (site,
+ * instance, installation, whatever).  A database cluster is a
+ * collection of PostgreSQL databases all managed by the same postmaster.
  *
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
+ * To create the database cluster, we create the directory that contains
+ * all its data, create the files that hold the global tables, create
+ * a few other control files for it, and create two databases: the
+ * template0 and template1 databases.
  *
- * This code is released under the terms of the PostgreSQL License.
+ * The template databases are ordinary PostgreSQL databases.  template0
+ * is never supposed to change after initdb, whereas template1 can be
+ * changed to add site-local standard data.  Either one can be copied
+ * to produce a new database.
  *
- * This is a C implementation of the previous shell script for setting up a
- * PostgreSQL cluster location, and should be highly compatible with it.
+ * To create template1, we run the postgres (backend) program in bootstrap
+ * mode and feed it data from the postgres.bki library file.  After this
+ * initial bootstrap phase, some additional stuff is created by normal
+ * SQL commands fed to a standalone backend.  Some of those commands are
+ * just embedded into this program (yeah, it's ugly), but larger chunks
+ * are taken from script files.
+ *
+ * template0 is made just by copying the completed template1.
  *
- * $Header: /cvsroot/pgsql/src/bin/initdb/initdb.c,v 1.6 2003/11/13 20:12:47 momjian Exp $
  *
  * TODO:
  *      - clean up find_postgres code and return values
  *
  * Note:
  *      The program has some memory leakage - it isn't worth cleaning it up.
- *      Even before the code was put in to free most of the dynamic memory
- *      used it ran around 500Kb used + malloc overhead. It should now use
- *      far less than that (around 240Kb - the size of the BKI file).
- *      If we can't load this much data into memory how will we ever run
- *      postgres anyway?
+ *
+ *
+ * This is a C implementation of the previous shell script for setting up a
+ * PostgreSQL cluster location, and should be highly compatible with it.
+ * author of C translation: Andrew Dunstan        mailto:andrew@dunslane.net
+ *
+ * This code is released under the terms of the PostgreSQL License.
+ *
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Header: /cvsroot/pgsql/src/bin/initdb/initdb.c,v 1.7 2003/11/13 23:46:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,8 +67,12 @@ int                  optreset;
 /* version string we expect back from postgres */
 #define PG_VERSIONSTR "postgres (PostgreSQL) " PG_VERSION "\n"
 
-/* values passed in by makefile define */
-
+/*
+ * these values are passed in by makefile defines
+ *
+ * Note that "datadir" is not the directory we're going to initialize,
+ * it's merely how Autoconf names PREFIX/share.
+ */
 char      *bindir = PGBINDIR;
 char      *datadir = PGDATADIR;
 
@@ -96,6 +119,16 @@ bool                not_ok = false;
 int                    n_connections = 10;
 int                    n_buffers = 50;
 
+/*
+ * Centralized knowledge of switches to pass to backend
+ *
+ * Note: in the shell-script version, we also passed PGDATA as a -D switch,
+ * but here it is more convenient to pass it as an environment variable
+ * (no quoting to worry about).
+ */
+static const char *boot_options = "-F";
+static const char *backend_options = "-F -O -c search_path=pg_catalog -c exit_on_error=true";
+
 
 /* platform specific path stuff */
 #if defined(__CYGWIN__) || defined(WIN32)
@@ -119,14 +152,11 @@ char         *pgpath;
 static bool rmtree(char *, bool);
 static void exit_nicely(void);
 static void canonicalize_path(char *);
-
 #ifdef WIN32
 static char *expanded_path(char *);
-
 #else
 #define expanded_path(x) (x)
 #endif
-
 static char **readfile(char *);
 static void writefile(char *, char **);
 static char *get_id(void);
@@ -181,7 +211,7 @@ do { \
 
 #define PG_CMD_CLOSE \
 do { \
-                if(pclose(pg) >> 8 & 0xff) \
+                if ((pclose(pg) >> 8) & 0xff) \
                        exit_nicely(); \
 } while (0)
 
@@ -200,9 +230,9 @@ do { \
 
 /*
  * routines to check mem allocations and fail noisily.
+ *
  * Note that we can't call exit_nicely() on a memory failure, as it calls
  * rmtree() which needs memory allocation. So we just exit with a bang.
- *
  */
 static void *
 xmalloc(size_t size)
@@ -237,7 +267,6 @@ xstrdup(const char *s)
  * assumes path points to a valid directory
  * deletes everything under path
  * if rmtopdir is true deletes the directory too
- *
  */
 static bool
 rmtree(char *path, bool rmtopdir)
@@ -260,10 +289,11 @@ rmtree(char *path, bool rmtopdir)
 /*
  * make all paths look like unix, with forward slashes
  * also strip any trailing slash.
+ *
  * The Windows command processor will accept suitably quoted paths
  * with forward slashes, but barfs badly with mixed forward and back
  * slashes. Removing the trailing slash on a path means we never get
- * ugly double slashes.
+ * ugly double slashes.  Don't remove a leading slash, though.
  */
 static void
 canonicalize_path(char *path)
@@ -277,13 +307,14 @@ canonicalize_path(char *path)
                        *p = '/';
 #endif
        }
-       if (p != path && *--p == '/')
+       if (p > path+1 && *--p == '/')
                *p = '\0';
 }
 
 /*
  * make a copy of the array of lines, with token replaced by replacement
  * the first time it occurs on each line.
+ *
  * This does most of what sed was used for in the shell script, but
  * doesn't need any regexp stuff.
  */
@@ -342,7 +373,6 @@ replace_token(char **lines, char *token, char *replacement)
 
 /*
  * get the lines from a text file
- *
  */
 static char **
 readfile(char *path)
@@ -405,7 +435,6 @@ readfile(char *path)
 
 /*
  * write an array of lines to a file
- *
  */
 static void
 writefile(char *path, char **lines)
@@ -435,7 +464,6 @@ writefile(char *path, char **lines)
  * this tries to build all the elements of a path to a directory a la mkdir -p
  * we assume the path is in canonical form, i.e. uses / as the separator
  * we also assume it isn't null.
- *
  */
 static int
 mkdir_p(char *path, mode_t omode)
@@ -540,7 +568,6 @@ mkdir_p(char *path, mode_t omode)
 /*
  * clean up any files we created on failure
  * if we created the data directory remove it too
- *
  */
 static void
 exit_nicely(void)
@@ -565,13 +592,19 @@ exit_nicely(void)
                                fprintf(stderr, "%s: failed\n", progname);
                }
        }
+       else
+       {
+        fprintf(stderr,
+                               "%s: data directory \"%s\" not removed at user's request\n",
+                               progname, pg_data);
+       }
        exit(1);
 }
 
 /*
  * find the current user using code lifted from pg_id.c
- * on unix make sure it isn't really root
  *
+ * on unix make sure it isn't really root
  */
 static char *
 get_id(void)
@@ -588,8 +621,9 @@ get_id(void)
        {
                fprintf(stderr,
                                "%s: cannot be run as root\n"
-                        "Please log in (using, e.g., \"su\") as the (unprivileged) "
-                               "user that will\n" "own the server process.\n",
+                               "Please log in (using, e.g., \"su\") as the "
+                               "(unprivileged) user that will\n"
+                               "own the server process.\n",
                                progname);
                exit(1);
        }
@@ -614,7 +648,6 @@ get_id(void)
 
 /*
  * get the encoding id for a given encoding name
- *
  */
 static char *
 get_encoding_id(char *encoding_name)
@@ -638,7 +671,6 @@ get_encoding_id(char *encoding_name)
 
 /*
  * get short version of VERSION
- *
  */
 static char *
 get_short_version(void)
@@ -675,7 +707,6 @@ get_short_version(void)
 
 /*
  * make sure the data directory either doesn't exist or is empty
- *
  */
 static bool
 check_data_dir(void)
@@ -710,7 +741,6 @@ check_data_dir(void)
 
 /*
  * make the data directory (or one of its subdirectories if subdir is not NULL)
- *
  */
 static bool
 mkdatadir(char *subdir)
@@ -738,7 +768,6 @@ mkdatadir(char *subdir)
 
 /*
  * set name of given input file variable under data directory
- *
  */
 static void
 set_input(char **dest, char *filename)
@@ -749,7 +778,6 @@ set_input(char **dest, char *filename)
 
 /*
  * check that given input file exists
- *
  */
 static void
 check_input(char *path)
@@ -771,7 +799,6 @@ check_input(char *path)
 /*
  * TODO - clean this up and handle the errors properly
  * don't overkill
- *
  */
 #define FIND_SUCCESS 0
 #define FIND_NOT_FOUND 1
@@ -784,7 +811,6 @@ check_input(char *path)
 /*
  * see if there is a postgres executable in the given path, and giving the
  * right version number
- *
  */
 static int
 find_postgres(char *path)
@@ -801,10 +827,10 @@ find_postgres(char *path)
        FILE       *pgver;
        int                     plen = strlen(path);
 
-       if (path[plen - 1] != '/')
-               snprintf(fn, MAXPGPATH, "%s/postgres%s", path, EXE);
+       if (plen > 0 && path[plen - 1] != '/')
+               snprintf(fn, sizeof(fn), "%s/postgres%s", path, EXE);
        else
-               snprintf(fn, MAXPGPATH, "%spostgres%s", path, EXE);
+               snprintf(fn, sizeof(fn), "%spostgres%s", path, EXE);
 
        if (stat(fn, &statbuf) != 0)
        {
@@ -826,7 +852,7 @@ find_postgres(char *path)
                return FIND_BAD_PERM;
 #endif
 
-       snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -V 2>%s", path, DEVNULL);
+       snprintf(cmd, sizeof(cmd), "\"%s/postgres\" -V 2>%s", path, DEVNULL);
 
        if ((pgver = popen(cmd, "r")) == NULL)
                return FIND_EXEC_ERR;
@@ -845,7 +871,6 @@ find_postgres(char *path)
 /*
  * Windows doesn't like relative paths to executables (other things work fine)
  * so we call its builtin function to expand them. Elsewhere this is a NOOP
- *
  */
 #ifdef WIN32
 static char *
@@ -853,7 +878,7 @@ expanded_path(char *path)
 {
        char            abspath[MAXPGPATH];
 
-       if (_fullpath(abspath, path, MAXPGPATH) == NULL)
+       if (_fullpath(abspath, path, sizeof(abspath)) == NULL)
        {
                perror("expanded path");
                return path;
@@ -865,16 +890,21 @@ expanded_path(char *path)
 
 /*
  * set the paths pointing to postgres
+ *
  * look for it in the same place we found this program, or in the environment
  * path, or in the configured bindir.
+ * We do it in this order because during upgrades users might move
+ * their trees to backup places, so the hard-wired bindir might be inaccurate.
  *
+ * XXX this needs work, as its error handling is vastly inferior to the
+ * shell-script version, in particular the case where a postgres executable
+ * is failing
  */
 static int
 set_paths(void)
 {
        if (testpath && !self_path)
        {
-
                char       *path,
                                   *cursor;
                int                     pathlen,
@@ -916,7 +946,7 @@ set_paths(void)
 
                for (i = 0; i < pathsegs; i++)
                {
-                       snprintf(buf, MAXPGPATH, "%s/%s%s", pathbits[i], progname, EXE);
+                       snprintf(buf, sizeof(buf), "%s/%s%s", pathbits[i], progname, EXE);
                        if (stat(buf, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
                        {
                                self_path = pathbits[i];
@@ -952,7 +982,6 @@ set_paths(void)
 /*
  * write out the PG_VERSION file in the data dir, or its subdirectory
  * if extrapath is not NULL
- *
  */
 static void
 set_short_version(char *short_version, char *extrapath)
@@ -977,7 +1006,6 @@ set_short_version(char *short_version, char *extrapath)
 
 /*
  * set up an empty config file so we can check buffers and connections
- *
  */
 static void
 set_null_conf(void)
@@ -993,17 +1021,13 @@ set_null_conf(void)
 
 /*
  * check how many connections we can sustain
- *
  */
 static void
 test_connections(void)
 {
-       char       *format =
-       "\"%s/postgres\" -boot -x 0 -F "
-       "-c shared_buffers=%d -c max_connections=%d template1 <%s >%s 2>&1";
        char            cmd[MAXPGPATH];
-       int                     conns[] = {100, 50, 40, 30, 20, 10};
-       int                     len = sizeof(conns) / sizeof(int);
+       static const int conns[] = {100, 50, 40, 30, 20, 10};
+       static const int len = sizeof(conns) / sizeof(int);
        int                     i,
                                status;
 
@@ -1012,8 +1036,13 @@ test_connections(void)
 
        for (i = 0; i < len; i++)
        {
-               snprintf(cmd, sizeof(cmd), format,
-                                pgpath, conns[i] * 5, conns[i], DEVNULL, DEVNULL);
+               snprintf(cmd, sizeof(cmd),
+                                "\"%s/postgres\" -boot -x0 %s "
+                                "-c shared_buffers=%d -c max_connections=%d template1 "
+                                "<%s >%s 2>&1",
+                                pgpath, boot_options,
+                                conns[i] * 5, conns[i],
+                                DEVNULL, DEVNULL);
                status = system(cmd);
                if (status == 0)
                        break;
@@ -1027,17 +1056,14 @@ test_connections(void)
 
 /*
  * check how many buffers we can run with
- *
  */
 static void
 test_buffers(void)
 {
-       char       *format =
-       "\"%s/postgres\"  -boot -x 0 -F "
-       "-c shared_buffers=%d -c max_connections=%d template1 <%s >%s 2>&1";
        char            cmd[MAXPGPATH];
-       int                     bufs[] = {1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 50};
-       int                     len = sizeof(bufs) / sizeof(int);
+       static const int bufs[] = {1000, 900, 800, 700, 600, 500,
+                                                          400, 300, 200, 100, 50};
+       static const int len = sizeof(bufs) / sizeof(int);
        int                     i,
                                status;
 
@@ -1046,7 +1072,12 @@ test_buffers(void)
 
        for (i = 0; i < len; i++)
        {
-               snprintf(cmd, sizeof(cmd), format, pgpath, bufs[i], n_connections,
+               snprintf(cmd, sizeof(cmd),
+                                "\"%s/postgres\" -boot -x0 %s "
+                                "-c shared_buffers=%d -c max_connections=%d template1 "
+                                "<%s >%s 2>&1",
+                                pgpath, boot_options,
+                                bufs[i], n_connections,
                                 DEVNULL, DEVNULL);
                status = system(cmd);
                if (status == 0)
@@ -1061,12 +1092,10 @@ test_buffers(void)
 
 /*
  * set up all the config files
- *
  */
 static void
 setup_config(void)
 {
-
        char      **conflines;
        char            repltok[100];
        char            path[MAXPGPATH];
@@ -1097,7 +1126,7 @@ setup_config(void)
        snprintf(repltok, sizeof(repltok), "lc_time = '%s'", lc_time);
        conflines = replace_token(conflines, "#lc_time = 'C'", repltok);
 
-       snprintf(path, MAXPGPATH, "%s/postgresql.conf", pg_data);
+       snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data);
 
        writefile(path, conflines);
        chmod(path, 0600);
@@ -1115,7 +1144,7 @@ setup_config(void)
                                                          "#host    all         all         ::1");
 #endif
 
-       snprintf(path, MAXPGPATH, "%s/pg_hba.conf", pg_data);
+       snprintf(path, sizeof(path), "%s/pg_hba.conf", pg_data);
 
        writefile(path, conflines);
        chmod(path, 0600);
@@ -1126,7 +1155,7 @@ setup_config(void)
 
        conflines = readfile(ident_file);
 
-       snprintf(path, MAXPGPATH, "%s/pg_ident.conf", pg_data);
+       snprintf(path, sizeof(path), "%s/pg_ident.conf", pg_data);
 
        writefile(path, conflines);
        chmod(path, 0600);
@@ -1138,8 +1167,7 @@ setup_config(void)
 
 
 /*
- * run the bootstrap code
- *
+ * run the BKI script in bootstrap mode to create template1
  */
 static void
 bootstrap_template1(char *short_version)
@@ -1158,7 +1186,10 @@ bootstrap_template1(char *short_version)
 
        bki_lines = readfile(bki_file);
 
-       snprintf(headerline, MAXPGPATH, "# PostgreSQL %s\n", short_version);
+       /* Check that bki file appears to be of the right version */
+
+       snprintf(headerline, sizeof(headerline), "# PostgreSQL %s\n",
+                        short_version);
 
        if (strcmp(headerline, *bki_lines) != 0)
        {
@@ -1176,21 +1207,22 @@ bootstrap_template1(char *short_version)
        bki_lines = replace_token(bki_lines, "ENCODING", encodingid);
 
        /*
-        * we could save the old environment here, and restore it afterwards,
-        * but there doesn't seem to be any point, especially as we have
-        * already called setlocale().
+        * Pass correct LC_xxx environment to bootstrap.
         *
+        * The shell script arranged to restore the LC settings afterwards,
+        * but there doesn't seem to be any compelling reason to do that.
         */
-       snprintf(cmd, MAXPGPATH, "LC_COLLATE=%s", lc_collate);
+       snprintf(cmd, sizeof(cmd), "LC_COLLATE=%s", lc_collate);
        putenv(xstrdup(cmd));
 
-       snprintf(cmd, MAXPGPATH, "LC_CTYPE=%s", lc_ctype);
+       snprintf(cmd, sizeof(cmd), "LC_CTYPE=%s", lc_ctype);
        putenv(xstrdup(cmd));
 
        putenv("LC_ALL");
 
-       snprintf(cmd, MAXPGPATH,
-               " \"%s/postgres\"  -boot -x1 -F %s template1", pgpath, talkargs);
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" -boot -x1 %s %s template1",
+                        pgpath, boot_options, talkargs);
 
        PG_CMD_OPEN;
 
@@ -1209,12 +1241,10 @@ bootstrap_template1(char *short_version)
 
 /*
  * set up the shadow password table
- *
  */
 static void
 setup_shadow(void)
 {
-
        char       *pg_shadow_setup[] = {
                /*
                 * Create a trigger so that direct updates to pg_shadow will be
@@ -1240,10 +1270,10 @@ setup_shadow(void)
        fputs("initializing pg_shadow ... ", stdout);
        fflush(stdout);
 
-       snprintf(cmd, MAXPGPATH,
-                        "\"%s/postgres\" -F -O -c search_path=pg_catalog "
-                        "-c exit_on_error=true template1 >%s",
-                        pgpath, DEVNULL);
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" %s template1 >%s",
+                        pgpath, backend_options,
+                        DEVNULL);
 
        PG_CMD_OPEN;
 
@@ -1257,7 +1287,6 @@ setup_shadow(void)
 
 /*
  * get the superuser password if required, and call postgres to set it
- *
  */
 static void
 get_set_pwd(void)
@@ -1278,12 +1307,13 @@ get_set_pwd(void)
        }
        free(pwd2);
 
-       printf("storing the password ... ");
+       printf("setting password ... ");
        fflush(stdout);
 
-       snprintf(cmd, MAXPGPATH,
-                        "\"%s/postgres\" -F -O -c search_path=pg_catalog "
-                        "-c exit_on_error=true template1 >%s", pgpath, DEVNULL);
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" %s template1 >%s",
+                        pgpath, backend_options,
+                        DEVNULL);
 
        PG_CMD_OPEN;
 
@@ -1297,12 +1327,12 @@ get_set_pwd(void)
 
        PG_CMD_CLOSE;
 
-       snprintf(pwdpath, MAXPGPATH, "%s/global/pg_pwd", pg_data);
+       snprintf(pwdpath, sizeof(pwdpath), "%s/global/pg_pwd", pg_data);
        if (stat(pwdpath, &statbuf) != 0 || !S_ISREG(statbuf.st_mode))
        {
                fprintf(stderr,
-                               "%s: The password file was not generated - "
-                               "please report this problem\n",
+                               "%s: The password file was not generated. "
+                               "Please report this problem.\n",
                                progname);
                exit_nicely();
        }
@@ -1312,7 +1342,6 @@ get_set_pwd(void)
 
 /*
  * toast sys tables
- *
  */
 static void
 unlimit_systables(void)
@@ -1335,9 +1364,10 @@ unlimit_systables(void)
        fputs("enabling unlimited row size for system tables ... ", stdout);
        fflush(stdout);
 
-       snprintf(cmd, MAXPGPATH,
-                        "\"%s/postgres\" -F -O -c search_path=pg_catalog "
-                        "-c exit_on_error=true template1 >%s", pgpath, DEVNULL);
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" %s template1 >%s",
+                        pgpath, backend_options,
+                        DEVNULL);
 
        PG_CMD_OPEN;
 
@@ -1351,7 +1381,6 @@ unlimit_systables(void)
 
 /*
  * set up pg_depend
- *
  */
 static void
 setup_depend(void)
@@ -1362,10 +1391,13 @@ setup_depend(void)
                 * the tables that the dependency code handles.  This is overkill
                 * (the system doesn't really depend on having every last weird
                 * datatype, for instance) but generating only the minimum
-                * required set of dependencies seems hard. Note that we
-                * deliberately do not pin the system views. First delete any
-                * already-made entries; PINs override all else, and must be the
-                * only entries for their objects.
+                * required set of dependencies seems hard.
+                *
+                * Note that we deliberately do not pin the system views, which
+                * haven't been created yet.
+                *
+                * First delete any already-made entries; PINs override all else, and
+                * must be the only entries for their objects.
                 */
                "DELETE FROM pg_depend;\n",
                "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
@@ -1405,10 +1437,10 @@ setup_depend(void)
        fputs("initializing pg_depend ... ", stdout);
        fflush(stdout);
 
-       snprintf(cmd, MAXPGPATH,
-                        "\"%s/postgres\" -F -O -c search_path=pg_catalog "
-                        "-c exit_on_error=true template1 >%s",
-                        pgpath, DEVNULL);
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" %s template1 >%s",
+                        pgpath, backend_options,
+                        DEVNULL);
 
        PG_CMD_OPEN;
 
@@ -1422,7 +1454,6 @@ setup_depend(void)
 
 /*
  * set up system views
- *
  */
 static void
 setup_sysviews(void)
@@ -1436,10 +1467,13 @@ setup_sysviews(void)
 
        sysviews_setup = readfile(system_views_file);
 
-       snprintf(cmd, MAXPGPATH,
-                        "\"%s/postgres\" -F -N -O -c search_path=pg_catalog "
-                        "-c exit_on_error=true template1 >%s",
-                        pgpath, DEVNULL);
+       /*
+        * We use -N here to avoid backslashing stuff in system_views.sql
+        */
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" %s -N template1 >%s",
+                        pgpath, backend_options,
+                        DEVNULL);
 
        PG_CMD_OPEN;
 
@@ -1458,60 +1492,45 @@ setup_sysviews(void)
 
 /*
  * load description data
- *
  */
 static void
 setup_description(void)
 {
-       char       *pg_description_setup1[] = {
-               "CREATE TEMP TABLE tmp_pg_description ( "
-               "       objoid oid, "
-               "       classname name, "
-               "       objsubid int4, "
-               "       description text) WITHOUT OIDS;\n",
-               "COPY tmp_pg_description FROM STDIN;\n",
-               NULL
-       };
-
-       char       *pg_description_setup2[] = {
-               "\\.\n",
-               "INSERT INTO pg_description "
-               " SELECT t.objoid, c.oid, t.objsubid, t.description "
-               "  FROM tmp_pg_description t, pg_class c "
-               "    WHERE c.relname = t.classname;\n",
-               NULL
-       };
-
-
-       PG_CMD_DECL;
-
-       char      **desc_lines;
+       PG_CMD_DECL_NOLINE;
+       int                     fres;
 
        fputs("loading pg_description ... ", stdout);
        fflush(stdout);
 
-       snprintf(cmd, MAXPGPATH,
-                        "\"%s/postgres\" -F -O -c search_path=pg_catalog "
-                        "-c exit_on_error=true template1 >%s",
-                        pgpath, DEVNULL);
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" %s template1 >%s",
+                        pgpath, backend_options,
+                        DEVNULL);
 
        PG_CMD_OPEN;
 
-       for (line = pg_description_setup1; *line != NULL; line++)
-               PG_CMD_PUTLINE;
-
-       desc_lines = readfile(desc_file);
-       for (line = desc_lines; *line != NULL; line++)
-       {
-               PG_CMD_PUTLINE;
-               free(*line);
-       }
-
-       free(desc_lines);
+       fres = fprintf(pg,
+                                  "CREATE TEMP TABLE tmp_pg_description ( "
+                                  "    objoid oid, "
+                                  "    classname name, "
+                                  "    objsubid int4, "
+                                  "    description text) WITHOUT OIDS;\n");
+       if (fres < 0)
+               exit_nicely();
 
+       fres = fprintf(pg,
+                                  "COPY tmp_pg_description FROM '%s';\n",
+                                  desc_file);
+       if (fres < 0)
+               exit_nicely();
 
-       for (line = pg_description_setup2; *line != NULL; line++)
-               PG_CMD_PUTLINE;
+       fres = fprintf(pg,
+                                  "INSERT INTO pg_description "
+                                  " SELECT t.objoid, c.oid, t.objsubid, t.description "
+                                  "  FROM tmp_pg_description t, pg_class c "
+                                  "    WHERE c.relname = t.classname;\n");
+       if (fres < 0)
+               exit_nicely();
 
        PG_CMD_CLOSE;
 
@@ -1520,7 +1539,6 @@ setup_description(void)
 
 /*
  * load conversion functions
- *
  */
 static void
 setup_conversion(void)
@@ -1532,10 +1550,10 @@ setup_conversion(void)
        fputs("creating conversions ... ", stdout);
        fflush(stdout);
 
-       snprintf(cmd, MAXPGPATH,
-                        "\"%s/postgres\" -F -O -c search_path=pg_catalog "
-                        "-c exit_on_error=true template1 >%s",
-                        pgpath, DEVNULL);
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" %s template1 >%s",
+                        pgpath, backend_options,
+                        DEVNULL);
 
        PG_CMD_OPEN;
 
@@ -1556,8 +1574,12 @@ setup_conversion(void)
 }
 
 /*
- * run privileges script
+ * Set up privileges
  *
+ * We set most system catalogs and built-in functions as world-accessible.
+ * Some objects may require different permissions by default, so we
+ * make sure we don't overwrite privilege sets that have already been
+ * set (NOT NULL).
  */
 static void
 setup_privileges(void)
@@ -1584,10 +1606,10 @@ setup_privileges(void)
        fputs("setting privileges on built-in objects ... ", stdout);
        fflush(stdout);
 
-       snprintf(cmd, MAXPGPATH,
-                        "\"%s/postgres\" -F -O -c search_path=pg_catalog "
-                        "-c exit_on_error=true template1 >%s",
-                        pgpath, DEVNULL);
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" %s template1 >%s",
+                        pgpath, backend_options,
+                        DEVNULL);
 
        PG_CMD_OPEN;
 
@@ -1602,8 +1624,8 @@ setup_privileges(void)
 }
 
 /*
- * extract the strange version of version required for info schema
- *
+ * extract the strange version of version required for information schema
+ * (09.08.0007abc)
  */
 static void
 set_info_version(void)
@@ -1631,14 +1653,11 @@ set_info_version(void)
 
 /*
  * load info schema and populate from features file
- *
  */
 static void
 setup_schema(void)
 {
-
        PG_CMD_DECL;
-
        char      **lines;
        int                     fres;
 
@@ -1648,13 +1667,12 @@ setup_schema(void)
        lines = readfile(info_schema_file);
 
        /*
-        * note that here we don't run in single line mode, unlike other
-        * places
+        * We use -N here to avoid backslashing stuff in information_schema.sql
         */
-       snprintf(cmd, MAXPGPATH,
-                        "\"%s/postgres\" -F -O -c search_path=pg_catalog "
-                        "-c exit_on_error=true -N template1 >%s",
-                        pgpath, DEVNULL);
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" %s -N template1 >%s",
+                        pgpath, backend_options,
+                        DEVNULL);
 
        PG_CMD_OPEN;
 
@@ -1668,20 +1686,10 @@ setup_schema(void)
 
        PG_CMD_CLOSE;
 
-       lines = readfile(features_file);
-
-       /*
-        * strip CR before NL this is the only place we do this  (following
-        * the shell script) - we could do it universally in readfile() if
-        * necessary
-        *
-        */
-       lines = replace_token(lines, "\r\n", "\n");
-
-       snprintf(cmd, MAXPGPATH,
-                        "\"%s/postgres\" -F -O -c search_path=pg_catalog "
-                        "-c exit_on_error=true template1 >%s",
-                        pgpath, DEVNULL);
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" %s template1 >%s",
+                        pgpath, backend_options,
+                        DEVNULL);
 
        PG_CMD_OPEN;
 
@@ -1690,31 +1698,18 @@ setup_schema(void)
                                   "  SET character_value = '%s' "
                                   "  WHERE implementation_info_name = 'DBMS VERSION';\n",
                                   infoversion);
-
        if (fres < 0)
                exit_nicely();
 
-       fres = fputs("COPY information_schema.sql_features "
-                                "  (feature_id, feature_name, sub_feature_id, "
-                                "  sub_feature_name, is_supported, comments) "
-                                "FROM STDIN;\n",
-                                pg);
-
+       fres = fprintf(pg,
+                                  "COPY information_schema.sql_features "
+                                  "  (feature_id, feature_name, sub_feature_id, "
+                                  "  sub_feature_name, is_supported, comments) "
+                                  " FROM '%s';\n",
+                                  features_file);
        if (fres < 0)
                exit_nicely();
 
-       for (line = lines; *line != NULL; line++)
-       {
-               PG_CMD_PUTLINE;
-               free(*line);
-       }
-
-       free(lines);
-
-       if (fputs("\\.\n", pg) < 0)
-               exit_nicely();
-       fflush(pg);
-
        PG_CMD_CLOSE;
 
        check_ok();
@@ -1722,7 +1717,6 @@ setup_schema(void)
 
 /*
  * clean everything up in template1
- *
  */
 static void
 vacuum_db(void)
@@ -1732,26 +1726,24 @@ vacuum_db(void)
        fputs("vacuuming database template1 ... ", stdout);
        fflush(stdout);
 
-       snprintf(cmd, MAXPGPATH,
-                        "\"%s/postgres\" -F -O -c search_path=pg_catalog "
-                        "-c exit_on_error=true template1 >%s",
-                        pgpath, DEVNULL);
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" %s template1 >%s",
+                        pgpath, backend_options,
+                        DEVNULL);
 
        PG_CMD_OPEN;
 
-       if (fputs("ANALYSE;\nVACUUM FULL FREEZE;\n", pg) < 0)
+       if (fputs("ANALYZE;\nVACUUM FULL FREEZE;\n", pg) < 0)
                exit_nicely();
        fflush(pg);
 
        PG_CMD_CLOSE;
 
        check_ok();
-
 }
 
 /*
  * copy template1 to template0
- *
  */
 static void
 make_template0(void)
@@ -1765,7 +1757,6 @@ make_template0(void)
 
                /*
                 * We use the OID of template0 to determine lastsysoid
-                *
                 */
                "UPDATE pg_database SET datlastsysoid = "
                "    (SELECT oid::int4 - 1 FROM pg_database "
@@ -1791,10 +1782,10 @@ make_template0(void)
        fputs("copying template1 to template0 ... ", stdout);
        fflush(stdout);
 
-       snprintf(cmd, MAXPGPATH,
-                        "\"%s/postgres\" -F -O -c search_path=pg_catalog "
-                        "-c exit_on_error=true template1 >%s",
-                        pgpath, DEVNULL);
+       snprintf(cmd, sizeof(cmd),
+                        "\"%s/postgres\" %s template1 >%s",
+                        pgpath, backend_options,
+                        DEVNULL);
 
        PG_CMD_OPEN;
 
@@ -1828,7 +1819,6 @@ make_template0(void)
  *
  * I have no idea how to handle this. (Strange they call UNIX an application!)
  * So this will need some testing on Windows.
- *
  */
 static void
 trapsig(int signum)
@@ -1840,26 +1830,25 @@ trapsig(int signum)
 
 /*
  * call exit_nicely() if we got a signal, or else output "ok".
- *
  */
 static void
 check_ok()
 {
        if (not_ok)
        {
-               puts("Caught Signal.");
+               printf("Caught Signal.\n");
                exit_nicely();
        }
        else
        {
                /* no signal caught */
-               puts("ok");
+               printf("ok\n");
        }
 }
 
 
 /*
- * check if given string is a valid locle specifier
+ * check if given string is a valid locale specifier
  * based on some code given to me by Peter Eisentraut
  * (but I take responsibility for it :-)
  */
@@ -1890,8 +1879,8 @@ chklocale(const char *locale)
 
 /*
  * set up the locale variables
- * assumes we have called setlocale(LC_ALL,"")
  *
+ * assumes we have called setlocale(LC_ALL,"")
  */
 static void
 setlocales(void)
@@ -1947,6 +1936,7 @@ setlocales(void)
 /*
  * help text data
  *
+ * Note: $CMDNAME is replaced by the right thing in usage()
  */
 char      *usage_text[] = {
        "$CMDNAME initializes a PostgreSQL database cluster.\n",
@@ -1983,15 +1973,12 @@ char       *usage_text[] = {
 };
 
 
-
 /*
  * print help text
- *
  */
 static void
 usage(void)
 {
-
        int                     i;
        char      **newtext;
 
@@ -2001,13 +1988,13 @@ usage(void)
                fputs(newtext[i], stdout);              /* faster than printf */
 }
 
+
 int
 main(int argc, char *argv[])
 {
        /*
         * options with no short version return a low integer, the rest return
         * their short version value
-        *
         */
        static struct option long_options[] = {
                {"pgdata", required_argument, NULL, 'D'},
@@ -2038,19 +2025,15 @@ main(int argc, char *argv[])
                                                                 * environment */
        char       *subdirs[] =
        {"global", "pg_xlog", "pg_clog", "base", "base/1"};
-
        char       *lastsep;
-
-       /* parse argv[0] - detect explicit path if there was one */
-
        char       *carg0;
-
 #if defined(__CYGWIN__) || defined(WIN32)
        char       *exe;                        /* location of exe suffix in progname */
 #endif
 
        setlocale(LC_ALL, "");
 
+       /* parse argv[0] - detect explicit path if there was one */
        carg0 = xstrdup(argv[0]);
        canonicalize_path(carg0);
 
@@ -2078,7 +2061,7 @@ main(int argc, char *argv[])
                self_path = NULL;
        }
 
-       /* process options */
+       /* process command-line options */
 
        while (1)
        {
@@ -2108,9 +2091,11 @@ main(int argc, char *argv[])
                                break;
                        case 'd':
                                debug = true;
+                printf("Running in debug mode.\n");
                                break;
                        case 'n':
                                noclean = true;
+                               printf("Running in noclean mode.  Mistakes will not be cleaned up.\n");
                                break;
                        case 'L':
                                datadir = xstrdup(optarg);
@@ -2155,15 +2140,30 @@ main(int argc, char *argv[])
 
        }
 
+       /* Non-option argument specifies data directory */
        if (optind < argc)
        {
                pg_data = xstrdup(argv[optind]);
                optind++;
        }
 
-       set_info_version();
+       if (optind < argc)
+               show_help = true;
+
+       if (show_version)
+       {
+               /* hard coded name here, in case they rename executable */
+               printf("initdb (PostgreSQL) %s\n", PG_VERSION);
+               exit(0);
+       }
 
-       if (strlen(pg_data) == 0 && !(show_help || show_setting))
+       if (show_help)
+       {
+               usage();
+               exit(0);
+       }
+
+       if (strlen(pg_data) == 0)
        {
                pgdenv = getenv("PGDATA");
                if (pgdenv && strlen(pgdenv))
@@ -2175,11 +2175,13 @@ main(int argc, char *argv[])
                {
                        fprintf(stderr,
                                        "%s: no data directory specified\n"
-                          "You must identify the directory where the data for this "
-                                       "database system\n"
-                          "will reside.  Do this with either the invocation option "
-                                       "-D or the\n" "environment variable PGDATA.\n",
+                                       "You must identify the directory where the data "
+                                       "for this database system\n"
+                                       "will reside.  Do this with either the invocation "
+                                       "option -D or the\n"
+                                       "environment variable PGDATA.\n",
                                        progname);
+                       exit(1);
                }
        }
 
@@ -2190,28 +2192,11 @@ main(int argc, char *argv[])
         * commnd line to avoid dumb quoting problems on Windows, and we would
         * expecially need quotes otherwise on Windows because paths there are
         * most likely to have embedded spaces.
-        *
         */
        pgdenv = xmalloc(8 + strlen(pg_data));
        sprintf(pgdenv, "PGDATA=%s", pg_data);
        putenv(pgdenv);
 
-       if (optind < argc)
-               show_help = true;
-
-       if (show_version)
-       {
-               /* hard coded name here, in case they rename executable */
-               printf("initdb (PostgreSQL) %s\n", PG_VERSION);
-               exit(0);
-       }
-
-       if (show_help)
-       {
-               usage();
-               exit(0);
-       }
-
        if (set_paths() != 0)
        {
                fprintf(stderr,
@@ -2220,10 +2205,8 @@ main(int argc, char *argv[])
                                "the directory \"%s\". Check your installation.\n",
                                progname, bindir);
                exit(1);
-
        }
 
-
        if ((short_version = get_short_version()) == NULL)
        {
                fprintf(stderr, "%s: could not get valid short version\n", progname);
@@ -2247,6 +2230,8 @@ main(int argc, char *argv[])
        set_input(&features_file, "sql_features.txt");
        set_input(&system_views_file, "system_views.sql");
 
+       set_info_version();
+
        if (show_setting || debug)
        {
                fprintf(stderr,
@@ -2262,11 +2247,10 @@ main(int argc, char *argv[])
                                username, bki_file,
                                desc_file, conf_file,
                                hba_file, ident_file);
+               if (show_setting)
+                       exit(0);
        }
 
-       if (show_setting)
-               exit(0);
-
        check_input(bki_file);
        check_input(desc_file);
        check_input(hba_file);
@@ -2338,12 +2322,10 @@ main(int argc, char *argv[])
        {
                fprintf(stderr,
                                "%s: directory \"%s\" exists but is not empty\n"
-                               "If you want to create a new database system, either "
-                               "remove or empty\n"
-                               "the directory \"$PGDATA\" or run $CMDNAME with an "
-                               "argument other than\n"
-                               "\"%s\".\n",
-                               progname, pg_data, pg_data);
+                               "If you want to create a new database system, either remove or empty\n"
+                               "the directory \"%s\" or run %s\n"
+                               "with an argument other than \"%s\".\n",
+                               progname, pg_data, pg_data, progname, pg_data);
                exit(1);
        }
 
@@ -2364,6 +2346,8 @@ main(int argc, char *argv[])
                made_new_pgdata = true;
        }
 
+       /* Create required subdirectories */
+
        for (i = 0; i < (sizeof(subdirs) / sizeof(char *)); i++)
        {
                printf("creating directory %s/%s ... ", pg_data, subdirs[i]);
@@ -2375,20 +2359,33 @@ main(int argc, char *argv[])
                        check_ok();
        }
 
+       /* Top level PG_VERSION is checked by bootstrapper, so make it first */
        set_short_version(short_version, NULL);
 
+       /*
+        * Determine platform-specific config settings
+        *
+        * Use reasonable values if kernel will let us, else scale back.  Probe for
+        * max_connections first since it is subject to more constraints than
+        * shared_buffers.
+        */
+
        set_null_conf();
 
-       /* test connections first because it has more constraints */
        test_connections();
        test_buffers();
 
+       /* Now create all the text config files */
        setup_config();
 
+       /* Bootstrap template1 */
        bootstrap_template1(short_version);
 
+       /* Make the per-database PGVERSION for template1 only after init'ing it */
        set_short_version(short_version, "base/1");
 
+       /* Create the stuff we don't need to use bootstrap mode for */
+
        setup_shadow();
        if (pwprompt)
                get_set_pwd();