From 18f58a048ebb08761a43e2c8cfd7fe2d69f2d93b Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 13 Nov 2003 23:46:31 +0000 Subject: [PATCH] Preliminary code review for C version of initdb. Re-centralize handling 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 | 535 ++++++++++++++++++++-------------------- 1 file changed, 266 insertions(+), 269 deletions(-) diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 46d64e08d4..43f8304e90 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -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(); -- 2.40.0