Thread: initdb in C
Andrew Dunstan wrote: > > Bruce, > > I think this is now ready to import into the WIN32 branch. I have ironed out > the cygwin dependencies, and posted a note about what we'll need to do > inside postgres to handle the environment values for setlocale() on Windows. > > If you want to rip out the unlink stuff (see previous email thread) we'll > need a version of dirmod.c that is compiled with -DFRONTEND - I guess libpq > is the obvious place for that. > > Apart from initdb.c we'll need the file system_views.sql, which can be taken > without change (unless someone want to add comments to it), and Makefile > modifications (see my Makefile on the web for an example, but it probably > doesn't comply with our standard way of doing things). > > Anything broken can then be fixed with patches - I have about 7 versions on > 5 machines now and my head is starting to swim -). > > pg_id and pg_encoding now become redundant in their entirety, as does > initdb.sh. Here is a slightly modified version of Andrew's great work in making a C version of initdb. Other than minor cleanups, the only big change was to remove rmdir handling because we using rm -r and rmdir /s in commands/dbcommands.c, so we might as use the same thing for initdb.c rather than having code that traverses the directory tree doing 'rm'. The other change was to remove the unlink code and instead use port/dirmod.c's version. It passes all the regression tests. I have also included a diff against Andrew's version so you can see my changes. It seems Andrew had a very current version of initdb. The only update he missed was the change to test the number of connections before shared buffers --- I made that change myself. I would like to apply this in the next few days to HEAD before initdb.sh drifts. We are no longer using the WIN32_DEV branch because all our work is now in 7.5 head. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073 /*------------------------------------------------------------------------- * * initdb * * author: Andrew Dunstan mailto:andrew@dunslane.net * * Copyright (C) 2003 Andrew Dunstan * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * This code is released under the terms of the PostgreSQL License. * * This is a C implementation of the previous shell script for setting up a * PostgreSQL cluster location, and should be highly compatible with it. * * $Header$ * * 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? * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "getopt_long.h" #include <dirent.h> #include <sys/stat.h> #include <unistd.h> #include <locale.h> #include <signal.h> #include "libpq/pqsignal.h" #include "mb/pg_wchar.h" /* version string we expect back from postgres */ #define PG_VERSIONSTR "postgres (PostgreSQL) " PG_VERSION "\n" /* values passed in by makefile define */ char *bindir = PGBINDIR; char *datadir = PGDATADIR; /* values to be obtained from arguments */ char *pg_data = ""; char *encoding = ""; char *locale = ""; char *lc_collate = ""; char *lc_ctype = ""; char *lc_monetary = ""; char *lc_numeric = ""; char *lc_time = ""; char *lc_messages = ""; char *username = ""; bool pwprompt = false; bool debug = false; bool noclean = false; bool show_help = false; bool show_version = false; bool show_setting = false; /* internal vars */ char *progname; char *self_path; char *postgres; char *encodingid = "0"; char *bki_file; char *desc_file; char *hba_file; char *ident_file; char *conf_file; char *conversion_file; char *info_schema_file; char *features_file; char *system_views_file; char *effective_user; bool testpath = true; bool made_new_pgdata = false; char infoversion[100]; bool not_ok = false; /* defaults */ int n_connections = 10; int n_buffers = 50; /* platform specific path stuff */ #if defined(__CYGWIN__) || defined(WIN32) #define EXE ".exe" #define DEVNULL "nul" #else #define EXE "" #define DEVNULL "/dev/null" #endif #ifdef WIN32 #define PATHSEP ';' #else #define PATHSEP ':' #endif /* detected path to postgres and (we assume) friends */ char *pgpath; /* forward declare all our functions */ static bool rmtree(char *, bool); static void exit_nicely(void); static void canonicalise_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); static char *get_encoding_id(char *); static char *get_short_version(void); static int mkdir_p(char *, mode_t); static bool check_data_dir(void); static bool mkdatadir(char *); static bool chklocale(const char *); static void setlocales(void); static void set_input(char **, char *); static void check_input(char *path); static int find_postgres(char *); static int set_paths(void); static char **replace_token(char **, char *, char *); static void set_short_version(char *, char *); static void set_null_conf(void); static void test_buffers(void); static void test_connections(void); static void setup_config(void); static void bootstrap_template1(char *); static void setup_shadow(void); static void get_set_pwd(void); static void unlimit_systables(void); static void setup_depend(void); static void setup_sysviews(void); static void setup_description(void); static void setup_conversion(void); static void setup_privileges(void); static void set_info_version(void); static void setup_schema(void); static void vacuum_db(void); static void make_template0(void); static void usage(void); static void trapsig(int); static void check_ok(void); static char *xstrdup(const char *); static void *xmalloc(size_t); /* * macros for running pipes to postgres */ #define PG_CMD_DECL char cmd[MAXPGPATH]; char ** line ; FILE * pg #define PG_CMD_DECL_NOLINE char cmd[MAXPGPATH]; FILE * pg #define PG_CMD_OPEN \ do { \ pg = popen(cmd,PG_BINARY_W); \ if (pg == NULL) \ exit_nicely(); \ } while (0) #define PG_CMD_CLOSE \ do { \ if(pclose(pg) >> 8 & 0xff) \ exit_nicely(); \ } while (0) #define PG_CMD_PUTLINE \ do { \ if (fputs(*line, pg) < 0) \ exit_nicely(); \ fflush(pg); \ } while (0) #ifndef WIN32 #define QUOTE_PATH "" #else #define QUOTE_PATH "\"" #endif /* * 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) { void *result; result = malloc(size); if (!result) { fputs("malloc failure - bailing out\n", stderr); exit(1); } return result; } static char * xstrdup(const char *s) { char *result; result = strdup(s); if (!result) { fputs("strdup failure - bailing out\n", stderr); exit(1); } return result; } /* * delete a directory tree recursively * 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) { char buf[MAXPGPATH + 64]; int ret = 1; #ifndef WIN32 /* doesn't handle .* files */ snprintf(buf, sizeof(buf), "rm -rf '%s%s'", path, (rmtopdir) ? "" : "/*"); #else snprintf(buf, sizeof(buf), "rmdir /s /q \"%s\"", path); #endif if (system(buf) != 0) ret = 0; #ifdef WIN32 if (ret == 1 && !rmtopdir) ret = !mkdir(path); /* recreate, no permission issues? */ #endif return ret; } /* * make all paths look like unix, with forward slashes * also strip any trailing slash * needed? */ static void canonicalise_path(char *path) { char *p; for (p = path; *p; p++) { #ifdef WIN32 if (*p == '\\') *p = '/'; #endif } if (p != path && *--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. */ static char ** replace_token(char **lines, char *token, char *replacement) { int numlines = 1; int i; char **result; int toklen, replen, diff; for (i = 0; lines[i]; i++) numlines++; result = (char **) xmalloc(numlines * sizeof(char *)); toklen = strlen(token); replen = strlen(replacement); diff = replen - toklen; for (i = 0; i < numlines; i++) { char *where; char *newline; int pre; /* just copy pointer if NULL or no change needed */ if (lines[i] == NULL || (where = strstr(lines[i], token)) == NULL) { result[i] = lines[i]; continue; } /* if we get here a change is needed - set up new line */ newline = (char *) xmalloc(strlen(lines[i]) + diff + 1); pre = where - lines[i]; strncpy(newline, lines[i], pre); strcpy(newline + pre, replacement); strcpy(newline + pre + replen, lines[i] + pre + toklen); result[i] = newline; } return result; } /* * get the lines from a text file * */ static char ** readfile(char *path) { FILE *infile; int maxlength = 0, linelen = 0; int nlines = 0; char **result; char *buffer; int c; if ((infile = fopen(path, "r")) == NULL) { fprintf(stderr, "could not read %s ... ", path); exit_nicely(); } /* pass over the file twice - the first time to size the result */ while ((c = fgetc(infile)) != EOF) { linelen++; if (c == '\n') { nlines++; if (linelen > maxlength) maxlength = linelen; linelen = 0; } } /* handle last line without a terminating newline (yuck) */ if (linelen) nlines++; if (linelen > maxlength) maxlength = linelen; /* set up the result and the line buffer */ result = (char **) xmalloc((nlines + 2) * sizeof(char *)); buffer = (char *) xmalloc(maxlength + 2); /* now reprocess the file and store the lines */ rewind(infile); nlines = 0; while (fgets(buffer, maxlength + 1, infile) != NULL) { result[nlines] = xstrdup(buffer); nlines++; } fclose(infile); result[nlines] = NULL; return result; } /* * write an array of lines to a file * */ static void writefile(char *path, char **lines) { FILE *out_file; char **line; ; if ((out_file = fopen(path, PG_BINARY_W)) == NULL) { fprintf(stderr, "could not write %s ... ", path); exit_nicely(); } for (line = lines; *line != NULL; line++) { if (fputs(*line, out_file) < 0) exit_nicely(); free(*line); } if (fclose(out_file)) exit_nicely(); } /* source stolen from FreeBSD /src/bin/mkdir/mkdir.c and adapted */ /* * 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) { struct stat sb; mode_t numask, oumask; int first, last, retval; char *p; p = path; oumask = 0; retval = 0; #ifdef WIN32 /* skip network and drive specifiers for win32 */ if (strlen(p) >= 2) { if (p[0] == '/' && p[1] == '/') { /* network drive */ p = strstr(p + 2, "/"); if (p == NULL) return 1; } else if (p[1] == ':' && ((p[0] >= 'a' && p[0] <= 'z') || (p[0] >= 'A' && p[0] <= 'Z'))) { /* local drive */ p += 2; } } #endif if (p[0] == '/') /* Skip leading '/'. */ ++p; for (first = 1, last = 0; !last; ++p) { if (p[0] == '\0') last = 1; else if (p[0] != '/') continue; *p = '\0'; if (p[1] == '\0') last = 1; if (first) { /* * POSIX 1003.2: For each dir operand that does not name an * existing directory, effects equivalent to those cased by * the following command shall occcur: * * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] * dir * * We change the user's umask and then restore it, instead of * doing chmod's. */ oumask = umask(0); numask = oumask & ~(S_IWUSR | S_IXUSR); (void) umask(numask); first = 0; } if (last) (void) umask(oumask); if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) { if (errno == EEXIST || errno == EISDIR) { if (stat(path, &sb) < 0) { retval = 1; break; } else if (!S_ISDIR(sb.st_mode)) { if (last) errno = EEXIST; else errno = ENOTDIR; retval = 1; break; } } else { retval = 1; break; } } if (!last) *p = '/'; } if (!first && !last) (void) umask(oumask); return retval; } /* * clean up any files we created on failure * if we created the data directory remove it too * */ static void exit_nicely(void) { fprintf(stderr, "%s: failed\n", progname); if (!noclean) { if (made_new_pgdata) { fprintf(stderr, "%s: removing data directory \"%s\"\n", progname, pg_data); if (!rmtree(pg_data, true)) fprintf(stderr, "%s: failed\n", progname); } else { fprintf(stderr, "%s: removing contents of data directory \"%s\"\n", progname, pg_data); if (!rmtree(pg_data, false)) fprintf(stderr, "%s: failed\n", progname); } } exit(1); } /* * find the current user using code lifted from pg_id.c * on unix make sure it isn't really root * */ static char * get_id(void) { #ifndef WIN32 struct passwd *pw; pw = getpwuid(getuid()); #ifndef __BEOS__ /* no root check on BEOS */ if (!geteuid()) /* 0 is root's uid */ { 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", progname); exit(1); } #endif #else /* the windows code */ struct passwd_win32 { int pw_uid; char pw_name[128]; } pass_win32; struct passwd_win32 *pw = &pass_win32; DWORD pwname_size = sizeof(pass_win32.pw_name) - 1; pw->pw_uid = 1; GetUserName(pw->pw_name, &pwname_size); #endif return xstrdup(pw->pw_name); } /* * get the encoding id for a given encoding name * */ static char * get_encoding_id(char *encoding_name) { int enc; char result[20]; if (encoding_name && *encoding_name) { if ((enc = pg_char_to_encoding(encoding_name)) >= 0 && pg_valid_server_encoding(encoding_name) >= 0) { sprintf(result, "%d", enc); return xstrdup(result); } } fprintf(stderr, "%s: \"%s\" is not a valid server encoding name\n", progname, encoding_name ? encoding_name : "(null)"); exit(1); } /* * get short version of VERSION * */ static char * get_short_version(void) { bool gotdot = false; int end; char *vr; vr = xstrdup(PG_VERSION); for (end = 0; vr[end] != '\0'; end++) { if (vr[end] == '.') { if (end == 0) return NULL; else if (gotdot) break; else gotdot = true; } else if (vr[end] < '0' || vr[end] > '9') { /* gone past digits and dots */ break; } } if (end == 0 || vr[end - 1] == '.' || !gotdot) return NULL; vr[end] = '\0'; return vr; } /* * make sure the data directory either doesn't exist or is empty * */ static bool check_data_dir(void) { DIR *chkdir; struct dirent *file; bool empty = true; chkdir = opendir(pg_data); if (!chkdir) return (errno == ENOENT); while ((file = readdir(chkdir)) != NULL) { if (strcmp(".", file->d_name) == 0 || strcmp("..", file->d_name) == 0) { /* skip this and parent directory */ continue; } else { empty = false; break; } } closedir(chkdir); return empty; } /* * make the data directory (or one of its subdirectories if subdir is not NULL) * */ static bool mkdatadir(char *subdir) { char *path; int res; path = xmalloc(strlen(pg_data) + 2 + (subdir == NULL ? 0 : strlen(subdir))); if (subdir != NULL) sprintf(path, "%s/%s", pg_data, subdir); else strcpy(path, pg_data); res = mkdir(path, 0700); if (res == 0) return true; else if (subdir == NULL || errno != ENOENT) return false; else return !mkdir_p(path, 0700); } /* * set name of given input file variable under data directory * */ static void set_input(char **dest, char *filename) { *dest = xmalloc(strlen(datadir) + strlen(filename) + 2); sprintf(*dest, "%s/%s", datadir, filename); } /* * check that given input file exists * */ static void check_input(char *path) { struct stat statbuf; if (stat(path, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) { fprintf(stderr, "%s: file \"%s\" not found\n" "This means you have a corrupted installation or identified\n" "the wrong directory with the invocation option -L.\n", progname, path); exit(1); } } /* * TODO - clean this up and handle the errors properly * don't overkill * */ #define FIND_SUCCESS 0 #define FIND_NOT_FOUND 1 #define FIND_STAT_ERR 2 #define FIND_NOT_REGFILE 3 #define FIND_BAD_PERM 4 #define FIND_EXEC_ERR 5 #define FIND_WRONG_VERSION 6 /* * see if there is a postgres executable in the given path, and giving the * right version number * */ static int find_postgres(char *path) { char fn[MAXPGPATH]; char cmd[MAXPGPATH]; char line[100]; #ifndef WIN32 int permmask = S_IROTH | S_IXOTH; #endif struct stat statbuf; FILE *pgver; int plen = strlen(path); if (path[plen - 1] != '/') snprintf(fn, MAXPGPATH, "%s/postgres%s", path, EXE); else snprintf(fn, MAXPGPATH, "%spostgres%s", path, EXE); if (stat(fn, &statbuf) != 0) { if (errno == ENOENT) return FIND_NOT_FOUND; else return FIND_STAT_ERR; } if (!S_ISREG(statbuf.st_mode)) return FIND_NOT_REGFILE; #ifndef WIN32 /* * Only unix requires this test, on WIN32 an .exe file should be * executable */ if ((statbuf.st_mode & permmask) != permmask) return FIND_BAD_PERM; #endif snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -V 2>%s", path, DEVNULL); if ((pgver = popen(cmd, "r")) == NULL) return FIND_EXEC_ERR; if (fgets(line, sizeof(line), pgver) == NULL) perror("fgets failure"); pclose(pgver); if (strcmp(line, PG_VERSIONSTR) != 0) return FIND_WRONG_VERSION; return FIND_SUCCESS; } /* * 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 * expanded_path(char *path) { char abspath[MAXPGPATH]; if (_fullpath(abspath, path, MAXPGPATH) == NULL) { perror("expanded path"); return path; } canonicalise_path(abspath); return xstrdup(abspath); } #endif /* * 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. * */ static int set_paths(void) { if (testpath && !self_path) { char *path, *cursor; int pathlen, i, pathsegs; char **pathbits; char buf[MAXPGPATH]; struct stat statbuf; path = xstrdup(getenv("PATH")); pathlen = strlen(path); for (i = 0, pathsegs = 1; i < pathlen; i++) { if (path[i] == PATHSEP) pathsegs++; } pathbits = (char **) xmalloc(pathsegs * sizeof(char *)); for (i = 0, pathsegs = 0, cursor = path; i <= pathlen; i++) { if (path[i] == PATHSEP || path[i] == 0) { path[i] = 0; if (strlen(cursor) == 0) { /* empty path segment means current directory */ pathbits[pathsegs] = xstrdup("."); } else { canonicalise_path(cursor); pathbits[pathsegs] = cursor; } pathsegs++; cursor = path + i + 1; } } for (i = 0; i < pathsegs; i++) { snprintf(buf, MAXPGPATH, "%s/%s%s", pathbits[i], progname, EXE); if (stat(buf, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) { self_path = pathbits[i]; break; } } } if (testpath && self_path && (find_postgres(expanded_path(self_path)) == 0)) { /* we found postgres on out own path */ pgpath = expanded_path(self_path); } else { /* look in the hardcoded bindir */ int res; char *cbindir; cbindir = xstrdup(bindir); canonicalise_path(cbindir); res = find_postgres(expanded_path(cbindir)); if (res == 0) pgpath = expanded_path(cbindir); else return 1; } return 0; } /* * 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) { FILE *version_file; char *path; if (extrapath == NULL) { path = xmalloc(strlen(pg_data) + 12); sprintf(path, "%s/PG_VERSION", pg_data); } else { path = xmalloc(strlen(pg_data) + strlen(extrapath) + 13); sprintf(path, "%s/%s/PG_VERSION", pg_data, extrapath); } version_file = fopen(path, PG_BINARY_W); fprintf(version_file, "%s\n", short_version); fclose(version_file); } /* * set up an empty config file so we can check buffers and connections * */ static void set_null_conf(void) { FILE *conf_file; char *path; path = xmalloc(strlen(pg_data) + 17); sprintf(path, "%s/postgresql.conf", pg_data); conf_file = fopen(path, PG_BINARY_W); fclose(conf_file); } /* * 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); int i, status; for (i = 0; i < len; i++) { snprintf(cmd, sizeof(cmd), format, pgpath, conns[i] * 5, conns[i], DEVNULL, DEVNULL); status = system(cmd); if (status == 0) break; } if (i >= len) i = len - 1; n_connections = conns[i]; printf("connections set to %d\n", n_connections); } /* * 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); int i, status; for (i = 0; i < len; i++) { snprintf(cmd, sizeof(cmd), format, pgpath, bufs[i], n_connections, DEVNULL, DEVNULL); status = system(cmd); if (status == 0) break; } if (i >= len) i = len - 1; n_buffers = bufs[i]; printf("buffers set to %d\n", n_buffers); } /* * set up all the config files * */ static void setup_config(void) { char **conflines; char repltok[100]; char path[MAXPGPATH]; fputs("creating configuration files ... ", stdout); /* postgresql.conf */ conflines = readfile(conf_file); snprintf(repltok, sizeof(repltok), "max_connections = %d", n_connections); conflines = replace_token(conflines, "#max_connections = 100", repltok); snprintf(repltok, sizeof(repltok), "shared_buffers = %d", n_buffers); conflines = replace_token(conflines, "#shared_buffers = 1000", repltok); snprintf(repltok, sizeof(repltok), "lc_messages = '%s'", lc_messages); conflines = replace_token(conflines, "#lc_messages = 'C'", repltok); snprintf(repltok, sizeof(repltok), "lc_monetary = '%s'", lc_monetary); conflines = replace_token(conflines, "#lc_monetary = 'C'", repltok); snprintf(repltok, sizeof(repltok), "lc_numeric = '%s'", lc_numeric); conflines = replace_token(conflines, "#lc_numeric = 'C'", repltok); 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); writefile(path, conflines); chmod(path, 0600); free(conflines); /* pg_hba.conf */ conflines = readfile(hba_file); #ifndef HAVE_IPV6 conflines = replace_token(conflines, "host all all ::1", "#host all all ::1"); #endif snprintf(path, MAXPGPATH, "%s/pg_hba.conf", pg_data); writefile(path, conflines); chmod(path, 0600); free(conflines); /* pg_ident.conf */ conflines = readfile(ident_file); snprintf(path, MAXPGPATH, "%s/pg_ident.conf", pg_data); writefile(path, conflines); chmod(path, 0600); free(conflines); check_ok(); } /* * run the bootstrap code * */ static void bootstrap_template1(char *short_version) { char *talkargs = ""; char **bki_lines; char headerline[MAXPGPATH]; PG_CMD_DECL; printf("creating template1 database in %s/base/1 ... ", pg_data); if (debug) talkargs = "-d 5"; bki_lines = readfile(bki_file); snprintf(headerline, MAXPGPATH, "# PostgreSQL %s\n", short_version); if (strcmp(headerline, *bki_lines) != 0) { fprintf(stderr, "%s: input file \"%s\" does not belong to PostgreSQL %s\n" "Check your installation or specify the correct path " "using the option -L.\n", progname, bki_file, PG_VERSION); exit_nicely(); } bki_lines = replace_token(bki_lines, "POSTGRES", effective_user); 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(). * */ snprintf(cmd, MAXPGPATH, "LC_COLLATE=%s", lc_collate); putenv(xstrdup(cmd)); snprintf(cmd, MAXPGPATH, "LC_CTYPE=%s", lc_ctype); putenv(xstrdup(cmd)); putenv("LC_ALL"); snprintf(cmd, MAXPGPATH, " \"%s/postgres\" -boot -x1 -F %s template1", pgpath, talkargs); PG_CMD_OPEN; for (line = bki_lines; *line != NULL; line++) { PG_CMD_PUTLINE; free(*line); } PG_CMD_CLOSE; free(bki_lines); check_ok(); } /* * 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 * written to the flat password/group files pg_pwd and pg_group */ "CREATE TRIGGER pg_sync_pg_pwd " " AFTER INSERT OR UPDATE OR DELETE ON pg_shadow " " FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();\n", "CREATE TRIGGER pg_sync_pg_group " " AFTER INSERT OR UPDATE OR DELETE ON pg_group " " FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();\n", /* * needs to be done before alter user, because alter user checks * that pg_shadow is secure ... */ "REVOKE ALL on pg_shadow FROM public;\n", NULL }; PG_CMD_DECL; fputs("initializing pg_shadow ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; for (line = pg_shadow_setup; *line != NULL; line++) PG_CMD_PUTLINE; PG_CMD_CLOSE; check_ok(); } /* * get the superuser password if required, and call postgres to set it * */ static void get_set_pwd(void) { PG_CMD_DECL_NOLINE; char *pwd1, *pwd2; char pwdpath[MAXPGPATH]; struct stat statbuf; pwd1 = simple_prompt("Enter new superuser password: ", 100, false); pwd2 = simple_prompt("Enter it again: ", 100, false); if (strcmp(pwd1, pwd2) != 0) { fprintf(stderr, "Passwords didn't match.\n"); exit_nicely(); } free(pwd2); printf("storing the password ... "); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; if (fprintf( pg, "ALTER USER \"%s\" WITH PASSWORD '%s';\n", username, pwd1) < 0) { /* write failure */ exit_nicely(); } fflush(pg); PG_CMD_CLOSE; snprintf(pwdpath, MAXPGPATH, "%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", progname); exit_nicely(); } check_ok(); } /* * toast sys tables * */ static void unlimit_systables(void) { char *systables_setup[] = { "ALTER TABLE pg_attrdef CREATE TOAST TABLE;\n", "ALTER TABLE pg_constraint CREATE TOAST TABLE;\n", "ALTER TABLE pg_database CREATE TOAST TABLE;\n", "ALTER TABLE pg_description CREATE TOAST TABLE;\n", "ALTER TABLE pg_group CREATE TOAST TABLE;\n", "ALTER TABLE pg_proc CREATE TOAST TABLE;\n", "ALTER TABLE pg_rewrite CREATE TOAST TABLE;\n", "ALTER TABLE pg_shadow CREATE TOAST TABLE;\n", "ALTER TABLE pg_statistic CREATE TOAST TABLE;\n", NULL }; PG_CMD_DECL; fputs("enabling unlimited row size for system tables ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; for (line = systables_setup; *line != NULL; line++) PG_CMD_PUTLINE; PG_CMD_CLOSE; check_ok(); } /* * set up pg_depend * */ static void setup_depend(void) { char *pg_depend_setup[] = { /* * Make PIN entries in pg_depend for all objects made so far in * 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. */ "DELETE FROM pg_depend;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_class;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_proc;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_type;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_cast;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_constraint;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_attrdef;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_language;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_operator;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_opclass;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_rewrite;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_trigger;\n", /* * restriction here to avoid pinning the public namespace */ "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_namespace " " WHERE nspname LIKE 'pg%';\n", NULL }; PG_CMD_DECL; fputs("initializing pg_depend ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; for (line = pg_depend_setup; *line != NULL; line++) PG_CMD_PUTLINE; PG_CMD_CLOSE; check_ok(); } /* * set up system views * */ static void setup_sysviews(void) { PG_CMD_DECL; char **sysviews_setup; fputs("creating system views ... ", stdout); 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); PG_CMD_OPEN; for (line = sysviews_setup; *line != NULL; line++) { PG_CMD_PUTLINE; free(*line); } PG_CMD_CLOSE; free(sysviews_setup); check_ok(); } /* * 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; fputs("loading pg_description ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, 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); for (line = pg_description_setup2; *line != NULL; line++) PG_CMD_PUTLINE; PG_CMD_CLOSE; check_ok(); } /* * load conversion functions * */ static void setup_conversion(void) { PG_CMD_DECL; char **conv_lines; fputs("creating conversions ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; conv_lines = readfile(conversion_file); for (line = conv_lines; *line != NULL; line++) { if (strstr(*line, "DROP CONVERSION") != *line) PG_CMD_PUTLINE; free(*line); } free(conv_lines); PG_CMD_CLOSE; check_ok(); } /* * run privileges script * */ static void setup_privileges(void) { char *privileges_setup[] = { "UPDATE pg_class " " SET relacl = '{\"=r/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' " " WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;\n", "UPDATE pg_proc " " SET proacl = '{\"=X/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' " " WHERE proacl IS NULL;\n", "UPDATE pg_language " " SET lanacl = '{\"=U/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' " " WHERE lanpltrusted;\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n", "GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n", NULL }; PG_CMD_DECL; char **priv_lines; fputs("setting privileges on builtin objects ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; priv_lines = replace_token(privileges_setup, "$POSTGRES_SUPERUSERNAME", username); for (line = priv_lines; *line != NULL; line++) PG_CMD_PUTLINE; PG_CMD_CLOSE; check_ok(); } /* * extract the strange version of version required for info schema * */ static void set_info_version(void) { char *letterversion; long major = 0, minor = 0, micro = 0; char *endptr; char *vstr = xstrdup(PG_VERSION); char *ptr; ptr = vstr + (strlen(vstr) - 1); while (ptr != vstr && (*ptr < '0' || *ptr > '9')) ptr--; letterversion = ptr + 1; major = strtol(vstr, &endptr, 10); if (*endptr) minor = strtol(endptr + 1, &endptr, 10); if (*endptr) micro = strtol(endptr + 1, &endptr, 10); snprintf(infoversion, sizeof(infoversion), "%02ld.%02ld.%04ld%s", major, minor, micro, letterversion); } /* * load info schema and populate from features file * */ static void setup_schema(void) { PG_CMD_DECL; char **lines; int fres; fputs("creating information schema ... ", stdout); lines = readfile(info_schema_file); /* * note that here we don't run in single line mode, unlike other * places */ snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true -N template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; for (line = lines; *line != NULL; line++) { PG_CMD_PUTLINE; free(*line); } free(lines); 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); PG_CMD_OPEN; fres = fprintf(pg, "UPDATE information_schema.sql_implementation_info " " 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); 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(); } /* * clean everything up in template1 * */ static void vacuum_db(void) { PG_CMD_DECL_NOLINE; fputs("vacuuming database template1 ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; if (fputs("ANALYSE;\nVACUUM FULL FREEZE;\n", pg) < 0) exit_nicely(); fflush(pg); PG_CMD_CLOSE; check_ok(); } /* * copy template1 to template0 * */ static void make_template0(void) { char *template0_setup[] = { "CREATE DATABASE template0;\n", "UPDATE pg_database SET " " datistemplate = 't', " " datallowconn = 'f' " " WHERE datname = 'template0';\n", /* * We use the OID of template0 to determine lastsysoid * */ "UPDATE pg_database SET datlastsysoid = " " (SELECT oid::int4 - 1 FROM pg_database " " WHERE datname = 'template0');\n", /* * Explicitly revoke public create-schema and create-temp-table * privileges in template1 and template0; else the latter would be * on by default */ "REVOKE CREATE,TEMPORARY ON DATABASE template1 FROM public;\n", "REVOKE CREATE,TEMPORARY ON DATABASE template0 FROM public;\n", /* * Finally vacuum to clean up dead rows in pg_database */ "VACUUM FULL pg_database;\n", NULL }; PG_CMD_DECL; fputs("copying template1 to template0 ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; for (line = template0_setup; *line; line++) PG_CMD_PUTLINE; PG_CMD_CLOSE; check_ok(); } /* * signal handler in case we are interrupted. * * The Windows runtime docs at * http://msdn.microsoft.com/library/en-us/vclib/html/_crt_signal.asp * specifically forbid a number of things being done from a signal handler, * including IO, memory allocation and system calls, and only allow jmpbuf * if you are handling SIGFPE. * * I avoided doing the forbidden things by setting a flag instead of calling * exit_nicely() directly. * * Also note the behaviour of Windows with SIGINT, which says this: * Note SIGINT is not supported for any Win32 application, including * Windows 98/Me and Windows NT/2000/XP. When a CTRL+C interrupt occurs, * Win32 operating systems generate a new thread to specifically handle * that interrupt. This can cause a single-thread application such as UNIX, * to become multithreaded, resulting in unexpected behavior. * * 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) { /* handle systems that reset the handler, like Windows (grr) */ pqsignal(signum, trapsig); not_ok = true; } /* * call exit_nicely() if we got a signal, or else output "ok". * */ static void check_ok() { if (not_ok) { puts("Caught Signal."); exit_nicely(); } else { /* no signal caught */ puts("ok"); } } /* * check if given string is a valid locle specifier * based on some code given to me by Peter Eisentraut * (but I take responsibility for it :-) */ static bool chklocale(const char *locale) { bool ret; int category = LC_CTYPE; char *save; save = setlocale(category, NULL); if (!save) return false; /* should not happen; */ save = xstrdup(save); ret = (setlocale(category, locale) != NULL); setlocale(category, save); free(save); /* should we exit here? */ if (!ret) fprintf(stderr, "%s: invalid locale name \"%s\"\n", progname, locale); return ret; } /* * set up the locale variables * assumes we have called setlocale(LC_ALL,"") * */ static void setlocales(void) { /* set empty lc_* values to locale config if set */ if (strlen(locale) > 0) { if (strlen(lc_ctype) == 0) lc_ctype = locale; if (strlen(lc_collate) == 0) lc_collate = locale; if (strlen(lc_numeric) == 0) lc_numeric = locale; if (strlen(lc_time) == 0) lc_time = locale; if (strlen(lc_monetary) == 0) lc_monetary = locale; if (strlen(lc_messages) == 0) lc_messages = locale; } /* * override absent/invalid config settings from initdb's locale * settings */ if (strlen(lc_ctype) == 0 || !chklocale(lc_ctype)) lc_ctype = xstrdup(setlocale(LC_CTYPE, NULL)); if (strlen(lc_collate) == 0 || !chklocale(lc_collate)) lc_collate = xstrdup(setlocale(LC_COLLATE, NULL)); if (strlen(lc_numeric) == 0 || !chklocale(lc_numeric)) lc_numeric = xstrdup(setlocale(LC_NUMERIC, NULL)); if (strlen(lc_time) == 0 || !chklocale(lc_time)) lc_time = xstrdup(setlocale(LC_TIME, NULL)); if (strlen(lc_monetary) == 0 || !chklocale(lc_monetary)) lc_monetary = xstrdup(setlocale(LC_MONETARY, NULL)); if (strlen(lc_messages) == 0 || !chklocale(lc_messages)) #ifdef LC_MESSAGES { /* when available get the current locale setting */ lc_messages = xstrdup(setlocale(LC_MESSAGES, NULL)); } #else { /* when not available, get the CTYPE setting */ lc_messages = xstrdup(setlocale(LC_CTYPE, NULL)); } #endif } /* * help text data * */ char *usage_text[] = { "$CMDNAME initializes a PostgreSQL database cluster.\n", "\n", "Usage:\n", " $CMDNAME [OPTION]... [DATADIR]\n", "\n", "Options:\n", " [-D, --pgdata=]DATADIR location for this database cluster\n", " -E, --encoding=ENCODING set default encoding for new databases\n", " --locale=LOCALE initialize database cluster with given locale\n", " --lc-collate, --lc-ctype, --lc-messages=LOCALE\n", " --lc-monetary, --lc-numeric, --lc-time=LOCALE\n", " initialize database cluster with given locale\n", " in the respective category (default taken from\n", " environment)\n", " --no-locale equivalent to --locale=C\n", " -U, --username=NAME database superuser name\n", " -W, --pwprompt prompt for a password for the new superuser\n", " -?, --help show this help, then exit\n", " -V, --version output version information, then exit\n", "\n", "Less commonly used options: \n", " -d, --debug generate lots of debugging output\n", " -s, --show show internal settings\n", " -L DIRECTORY where to find the input files\n", " -n, --noclean do not clean up after errors\n", "\n", "If the data directory is not specified, the environment variable PGDATA\n", "is used.\n", "\n", "Report bugs to <pgsql-bugs@postgresql.org>.\n", NULL }; /* * print help text * */ static void usage(void) { int i; char **newtext; newtext = replace_token(usage_text, "$CMDNAME", progname); for (i = 0; newtext[i]; i++) 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'}, {"encoding", required_argument, NULL, 'E'}, {"locale", required_argument, NULL, 1}, {"lc-collate", required_argument, NULL, 2}, {"lc-ctype", required_argument, NULL, 3}, {"lc-monetary", required_argument, NULL, 4}, {"lc-numeric", required_argument, NULL, 5}, {"lc-time", required_argument, NULL, 6}, {"lc-messages", required_argument, NULL, 7}, {"no-locale", no_argument, NULL, 8}, {"pwprompt", no_argument, NULL, 'W'}, {"username", required_argument, NULL, 'U'}, {"help", no_argument, NULL, '?'}, {"version", no_argument, NULL, 'V'}, {"debug", no_argument, NULL, 'd'}, {"show", no_argument, NULL, 's'}, {"noclean", no_argument, NULL, 'n'}, {0, 0, 0, 0} }; int c, i; int option_index; char *short_version; char *pgdenv; /* PGDATA value got from sent to * 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, ""); carg0 = xstrdup(argv[0]); canonicalise_path(carg0); lastsep = strrchr(carg0, '/'); progname = lastsep ? xstrdup(lastsep + 1) : carg0; #if defined(__CYGWIN__) || defined(WIN32) if (strlen(progname) > 4 && (exe = progname + (strlen(progname) - 4)) && stricmp(exe, EXE) == 0) { /* strip .exe suffix, regardless of case */ *exe = '\0'; } #endif if (lastsep) { self_path = carg0; *lastsep = '\0'; } else { /* no path known to ourselves from argv[0] */ self_path = NULL; } /* process options */ while (1) { /* * a : as the first option char here lets us use ? as a short * option */ c = getopt_long(argc, argv, ":D:E:WU:?sVdnL:", long_options, &option_index); if (c == -1) break; switch (c) { case 'D': pg_data = xstrdup(optarg); break; case 'E': encoding = xstrdup(optarg); break; case 'W': pwprompt = true; break; case 'U': username = xstrdup(optarg); break; case 'd': debug = true; break; case 'n': noclean = true; break; case 'L': datadir = xstrdup(optarg); break; case 1: locale = xstrdup(optarg); break; case 2: lc_collate = xstrdup(optarg); break; case 3: lc_ctype = xstrdup(optarg); break; case 4: lc_monetary = xstrdup(optarg); break; case 5: lc_numeric = xstrdup(optarg); break; case 6: lc_time = xstrdup(optarg); break; case 7: lc_messages = xstrdup(optarg); break; case 8: locale = "C"; break; case '?': show_help = true; break; case 's': show_setting = true; break; case 'V': show_version = true; break; default: show_help = true; printf("Unrecognized option: %c\n", c); } } if (optind < argc) { pg_data = xstrdup(argv[optind]); optind++; } set_info_version(); if (strlen(pg_data) == 0 && !(show_help || show_setting)) { pgdenv = getenv("PGDATA"); if (pgdenv && strlen(pgdenv)) { /* PGDATA found */ pg_data = xstrdup(pgdenv); } else { 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", progname); } } canonicalise_path(pg_data); /* * we have to set PGDATA for postgres rather than pass it on the * 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, "The program \"postgres\" is needed by %s " "but was not found in \n" "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); exit(1); } effective_user = get_id(); if (!strlen(username)) username = effective_user; if (strlen(encoding)) encodingid = get_encoding_id(encoding); set_input(&bki_file, "postgres.bki"); set_input(&desc_file, "postgres.description"); set_input(&hba_file, "pg_hba.conf.sample"); set_input(&ident_file, "pg_ident.conf.sample"); set_input(&conf_file, "postgresql.conf.sample"); set_input(&conversion_file, "conversion_create.sql"); set_input(&info_schema_file, "information_schema.sql"); set_input(&features_file, "sql_features.txt"); set_input(&system_views_file, "system_views.sql"); if (show_setting || debug) { fprintf(stderr, "VERSION=%s\n" "PGDATA=%s\ndatadir=%s\nPGPATH=%s\n" "ENCODING=%s\nENCODINGID=%s\n" "POSTGRES_SUPERUSERNAME=%s\nPOSTGRES_BKI=%s\n" "POSTGRES_DESCR=%s\nPOSTGRESQL_CONF_SAMPLE=%s\n" "PG_HBA_SAMPLE=%s\nPG_IDENT_SAMPLE=%s\n", PG_VERSION, pg_data, datadir, pgpath, encoding, encodingid, username, bki_file, desc_file, conf_file, hba_file, ident_file); } if (show_setting) exit(0); check_input(bki_file); check_input(desc_file); check_input(hba_file); check_input(ident_file); check_input(conf_file); check_input(conversion_file); check_input(info_schema_file); check_input(features_file); check_input(system_views_file); setlocales(); if (strcmp(lc_ctype, lc_collate) == 0 && strcmp(lc_ctype, lc_time) == 0 && strcmp(lc_ctype, lc_numeric) == 0 && strcmp(lc_ctype, lc_monetary) == 0 && strcmp(lc_ctype, lc_messages) == 0) { printf("The database cluster will be initialized with locale %s\n", lc_ctype); } else { printf("The database cluster will be initialized with locales\n" " COLLATE: %s\n" " CTYPE: %s\n" " MESSAGES: %s\n" " MONETARY: %s\n" " NUMERIC: %s\n" " TIME: %s\n", lc_collate, lc_ctype, lc_messages, lc_monetary, lc_numeric, lc_time); } umask(077); /* * now we are starting to do real work, trap signals so we can clean * up */ /* some of these are not valid on Windows */ #ifdef SIGHUP pqsignal(SIGHUP, trapsig); #endif #ifdef SIGINT pqsignal(SIGINT, trapsig); #endif #ifdef SIGQUIT pqsignal(SIGQUIT, trapsig); #endif #ifdef SIGTERM pqsignal(SIGTERM, trapsig); #endif /* clear this we'll use it in a few lines */ errno = 0; if (!check_data_dir()) { 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); exit(1); } /* * check_data_dir() called opendir - the errno should still be hanging * around */ if (errno == ENOENT) { printf("creating directory \"%s\" ... ", pg_data); if (!mkdatadir(NULL)) exit_nicely(); else check_ok(); made_new_pgdata = true; } for (i = 0; i < (sizeof(subdirs) / sizeof(char *)); i++) { printf("creating directory %s/%s ... ", pg_data, subdirs[i]); if (!mkdatadir(subdirs[i])) exit_nicely(); else check_ok(); } set_short_version(short_version, NULL); set_null_conf(); /* test connections first because it has more constraints */ test_connections(); test_buffers(); setup_config(); bootstrap_template1(short_version); set_short_version(short_version, "base/1"); setup_shadow(); if (pwprompt) get_set_pwd(); unlimit_systables(); setup_depend(); setup_sysviews(); setup_description(); setup_conversion(); setup_privileges(); setup_schema(); vacuum_db(); make_template0(); printf("\n" "Success. You can now start the database server using:\n\n" " %s/postmaster -D %s%s%s\n" "or\n" " %s/pg_ctl -D %s%s%s -l logfile start\n\n", pgpath, QUOTE_PATH, pg_data, QUOTE_PATH, pgpath, QUOTE_PATH, pg_data, QUOTE_PATH); return 0; } CREATE VIEW pg_user AS SELECT usename, usesysid, usecreatedb, usesuper, usecatupd, '********'::text as passwd, valuntil, useconfig FROM pg_shadow; CREATE VIEW pg_rules AS SELECT N.nspname AS schemaname, C.relname AS tablename, R.rulename AS rulename, pg_get_ruledef(R.oid) AS definition FROM (pg_rewrite R JOIN pg_class C ON (C.oid = R.ev_class)) LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE R.rulename != '_RETURN'; CREATE VIEW pg_views AS SELECT N.nspname AS schemaname, C.relname AS viewname, pg_get_userbyid(C.relowner) AS viewowner, pg_get_viewdef(C.oid) AS definition FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE C.relkind = 'v'; CREATE VIEW pg_tables AS SELECT N.nspname AS schemaname, C.relname AS tablename, pg_get_userbyid(C.relowner) AS tableowner, C.relhasindex AS hasindexes, C.relhasrules AS hasrules, (C.reltriggers > 0) AS hastriggers FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE C.relkind = 'r'; CREATE VIEW pg_indexes AS SELECT N.nspname AS schemaname, C.relname AS tablename, I.relname AS indexname, pg_get_indexdef(I.oid) AS indexdef FROM pg_index X JOIN pg_class C ON (C.oid = X.indrelid) JOIN pg_class I ON (I.oid = X.indexrelid) LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE C.relkind = 'r' AND I.relkind = 'i'; CREATE VIEW pg_stats AS SELECT nspname AS schemaname, relname AS tablename, attname AS attname, stanullfrac AS null_frac, stawidth AS avg_width, stadistinct AS n_distinct, CASE 1 WHEN stakind1 THEN stavalues1 WHEN stakind2 THEN stavalues2 WHEN stakind3 THEN stavalues3 WHEN stakind4 THEN stavalues4 END AS most_common_vals, CASE 1 WHEN stakind1 THEN stanumbers1 WHEN stakind2 THEN stanumbers2 WHEN stakind3 THEN stanumbers3 WHEN stakind4 THEN stanumbers4 END AS most_common_freqs, CASE 2 WHEN stakind1 THEN stavalues1 WHEN stakind2 THEN stavalues2 WHEN stakind3 THEN stavalues3 WHEN stakind4 THEN stavalues4 END AS histogram_bounds, CASE 3 WHEN stakind1 THEN stanumbers1[1] WHEN stakind2 THEN stanumbers2[1] WHEN stakind3 THEN stanumbers3[1] WHEN stakind4 THEN stanumbers4[1] END AS correlation FROM pg_statistic s JOIN pg_class c ON (c.oid = s.starelid) JOIN pg_attribute a ON (c.oid = attrelid AND attnum = s.staattnum) LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) WHERE has_table_privilege(c.oid, 'select'); REVOKE ALL on pg_statistic FROM public; CREATE VIEW pg_stat_all_tables AS SELECT C.oid AS relid, N.nspname AS schemaname, C.relname AS relname, pg_stat_get_numscans(C.oid) AS seq_scan, pg_stat_get_tuples_returned(C.oid) AS seq_tup_read, sum(pg_stat_get_numscans(I.indexrelid)) AS idx_scan, sum(pg_stat_get_tuples_fetched(I.indexrelid)) AS idx_tup_fetch, pg_stat_get_tuples_inserted(C.oid) AS n_tup_ins, pg_stat_get_tuples_updated(C.oid) AS n_tup_upd, pg_stat_get_tuples_deleted(C.oid) AS n_tup_del FROM pg_class C LEFT JOIN pg_index I ON C.oid = I.indrelid LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE C.relkind = 'r' GROUP BY C.oid, N.nspname, C.relname; CREATE VIEW pg_stat_sys_tables AS SELECT * FROM pg_stat_all_tables WHERE schemaname IN ('pg_catalog', 'pg_toast'); CREATE VIEW pg_stat_user_tables AS SELECT * FROM pg_stat_all_tables WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); CREATE VIEW pg_statio_all_tables AS SELECT C.oid AS relid, N.nspname AS schemaname, C.relname AS relname, pg_stat_get_blocks_fetched(C.oid) - pg_stat_get_blocks_hit(C.oid) AS heap_blks_read, pg_stat_get_blocks_hit(C.oid) AS heap_blks_hit, sum(pg_stat_get_blocks_fetched(I.indexrelid) - pg_stat_get_blocks_hit(I.indexrelid)) AS idx_blks_read, sum(pg_stat_get_blocks_hit(I.indexrelid)) AS idx_blks_hit, pg_stat_get_blocks_fetched(T.oid) - pg_stat_get_blocks_hit(T.oid) AS toast_blks_read, pg_stat_get_blocks_hit(T.oid) AS toast_blks_hit, pg_stat_get_blocks_fetched(X.oid) - pg_stat_get_blocks_hit(X.oid) AS tidx_blks_read, pg_stat_get_blocks_hit(X.oid) AS tidx_blks_hit FROM pg_class C LEFT JOIN pg_index I ON C.oid = I.indrelid LEFT JOIN pg_class T ON C.reltoastrelid = T.oid LEFT JOIN pg_class X ON T.reltoastidxid = X.oid LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE C.relkind = 'r' GROUP BY C.oid, N.nspname, C.relname, T.oid, X.oid; CREATE VIEW pg_statio_sys_tables AS SELECT * FROM pg_statio_all_tables WHERE schemaname IN ('pg_catalog', 'pg_toast'); CREATE VIEW pg_statio_user_tables AS SELECT * FROM pg_statio_all_tables WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); CREATE VIEW pg_stat_all_indexes AS SELECT C.oid AS relid, I.oid AS indexrelid, N.nspname AS schemaname, C.relname AS relname, I.relname AS indexrelname, pg_stat_get_numscans(I.oid) AS idx_scan, pg_stat_get_tuples_returned(I.oid) AS idx_tup_read, pg_stat_get_tuples_fetched(I.oid) AS idx_tup_fetch FROM pg_class C JOIN pg_index X ON C.oid = X.indrelid JOIN pg_class I ON I.oid = X.indexrelid LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE C.relkind = 'r'; CREATE VIEW pg_stat_sys_indexes AS SELECT * FROM pg_stat_all_indexes WHERE schemaname IN ('pg_catalog', 'pg_toast'); CREATE VIEW pg_stat_user_indexes AS SELECT * FROM pg_stat_all_indexes WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); CREATE VIEW pg_statio_all_indexes AS SELECT C.oid AS relid, I.oid AS indexrelid, N.nspname AS schemaname, C.relname AS relname, I.relname AS indexrelname, pg_stat_get_blocks_fetched(I.oid) - pg_stat_get_blocks_hit(I.oid) AS idx_blks_read, pg_stat_get_blocks_hit(I.oid) AS idx_blks_hit FROM pg_class C JOIN pg_index X ON C.oid = X.indrelid JOIN pg_class I ON I.oid = X.indexrelid LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE C.relkind = 'r'; CREATE VIEW pg_statio_sys_indexes AS SELECT * FROM pg_statio_all_indexes WHERE schemaname IN ('pg_catalog', 'pg_toast'); CREATE VIEW pg_statio_user_indexes AS SELECT * FROM pg_statio_all_indexes WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); CREATE VIEW pg_statio_all_sequences AS SELECT C.oid AS relid, N.nspname AS schemaname, C.relname AS relname, pg_stat_get_blocks_fetched(C.oid) - pg_stat_get_blocks_hit(C.oid) AS blks_read, pg_stat_get_blocks_hit(C.oid) AS blks_hit FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE C.relkind = 'S'; CREATE VIEW pg_statio_sys_sequences AS SELECT * FROM pg_statio_all_sequences WHERE schemaname IN ('pg_catalog', 'pg_toast'); CREATE VIEW pg_statio_user_sequences AS SELECT * FROM pg_statio_all_sequences WHERE schemaname NOT IN ('pg_catalog', 'pg_toast'); CREATE VIEW pg_stat_activity AS SELECT D.oid AS datid, D.datname AS datname, pg_stat_get_backend_pid(S.backendid) AS procpid, pg_stat_get_backend_userid(S.backendid) AS usesysid, U.usename AS usename, pg_stat_get_backend_activity(S.backendid) AS current_query, pg_stat_get_backend_activity_start(S.backendid) AS query_start FROM pg_database D, (SELECT pg_stat_get_backend_idset() AS backendid) AS S, pg_shadow U WHERE pg_stat_get_backend_dbid(S.backendid) = D.oid AND pg_stat_get_backend_userid(S.backendid) = U.usesysid; CREATE VIEW pg_stat_database AS SELECT D.oid AS datid, D.datname AS datname, pg_stat_get_db_numbackends(D.oid) AS numbackends, pg_stat_get_db_xact_commit(D.oid) AS xact_commit, pg_stat_get_db_xact_rollback(D.oid) AS xact_rollback, pg_stat_get_db_blocks_fetched(D.oid) - pg_stat_get_db_blocks_hit(D.oid) AS blks_read, pg_stat_get_db_blocks_hit(D.oid) AS blks_hit FROM pg_database D; CREATE VIEW pg_locks AS SELECT * FROM pg_lock_status() AS L(relation oid, database oid, transaction xid, pid int4, mode text, granted boolean); CREATE VIEW pg_settings AS SELECT * FROM pg_show_all_settings() AS A (name text, setting text, context text, vartype text, source text, min_val text, max_val text); CREATE RULE pg_settings_u AS ON UPDATE TO pg_settings WHERE new.name = old.name DO SELECT set_config(old.name, new.setting, 'f'); CREATE RULE pg_settings_n AS ON UPDATE TO pg_settings DO INSTEAD NOTHING; Index: src/bin/initdb/Makefile =================================================================== RCS file: /cvsroot/pgsql-server/src/bin/initdb/Makefile,v retrieving revision 1.29 diff -c -c -r1.29 Makefile *** src/bin/initdb/Makefile 7 Sep 2003 03:36:03 -0000 1.29 --- src/bin/initdb/Makefile 8 Nov 2003 04:57:44 -0000 *************** *** 13,37 **** top_builddir = ../../.. include $(top_builddir)/src/Makefile.global ! all: initdb ! initdb: initdb.sh $(top_builddir)/src/Makefile.global ! sed -e 's/@VERSION@/$(VERSION)/g' \ ! -e 's,@SHELL@,$(SHELL),g' \ ! -e 's,@HAVE_IPV6@,$(HAVE_IPV6),g' \ ! -e 's,@bindir@,$(bindir),g' \ ! -e 's,@datadir@,$(datadir),g' \ ! $< >$@ ! chmod a+x $@ install: all installdirs ! $(INSTALL_SCRIPT) initdb $(DESTDIR)$(bindir)/initdb installdirs: $(mkinstalldirs) $(DESTDIR)$(bindir) uninstall: ! rm -f $(DESTDIR)$(bindir)/initdb ! clean distclean maintainer-clean: ! rm -f initdb --- 13,42 ---- top_builddir = ../../.. include $(top_builddir)/src/Makefile.global ! override CPPFLAGS := -DPGBINDIR=\"$(bindir)\" -DPGDATADIR=\"$(datadir)\" -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS) ! OBJS= initdb.o sprompt.o ! ifeq ($(PORTNAME), win32) ! OBJS+=dirmod.o ! endif ! ! all: submake-libpq submake-libpgport initdb ! ! initdb: $(OBJS) $(libpq_builddir)/libpq.a ! $(CC) $(CFLAGS) $(OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@ ! ! dirmod.c sprompt.c: % : $(top_srcdir)/src/port/% ! rm -f $@ && $(LN_S) $< . install: all installdirs ! $(INSTALL_PROGRAM) initdb$(X) $(DESTDIR)$(bindir)/initdb$(X) ! $(INSTALL_DATA) $(srcdir)/system_views.sql $(DESTDIR)$(datadir)/system_views.sql installdirs: $(mkinstalldirs) $(DESTDIR)$(bindir) uninstall: ! rm -f $(DESTDIR)$(bindir)/initdb$(X) $(DESTDIR)$(datadir)/system_views.sql ! clean distclean: ! rm -f initdb$(X) $(OBJS) *** /laptop/initdb.c Sat Nov 8 00:10:58 2003 --- initdb.c Fri Nov 7 23:55:07 2003 *************** *** 1,4 **** - /*------------------------------------------------------------------------- * * initdb --- 1,3 ---- *************** *** 53,59 **** char *datadir = PGDATADIR; /* values to be obtained from arguments */ - char *pg_data = ""; char *encoding = ""; char *locale = ""; --- 52,57 ---- *************** *** 93,100 **** bool not_ok = false; /* defaults */ - int n_buffers = 50; int n_connections = 10; /* platform specific path stuff */ --- 91,98 ---- bool not_ok = false; /* defaults */ int n_connections = 10; + int n_buffers = 50; /* platform specific path stuff */ *************** *** 104,116 **** #else #define EXE "" #define DEVNULL "/dev/null" ! #endif /* defined(__CYGWIN__) || defined(WIN32) */ #ifdef WIN32 #define PATHSEP ';' #else #define PATHSEP ':' ! #endif /* WIN32 */ /* detected path to postgres and (we assume) friends */ char *pgpath; --- 102,114 ---- #else #define EXE "" #define DEVNULL "/dev/null" ! #endif #ifdef WIN32 #define PATHSEP ';' #else #define PATHSEP ':' ! #endif /* detected path to postgres and (we assume) friends */ char *pgpath; *************** *** 122,140 **** #ifdef WIN32 static char *expanded_path(char *); - static int init_unlink(const char *); - #else ! #define expanded_path(x) ( x ) ! #define init_unlink(x) unlink( (x) ) ! #endif /* WIN32 */ static char **readfile(char *); static void writefile(char *, char **); static char *get_id(void); static char *get_encoding_id(char *); static char *get_short_version(void); ! static int build_path(char *, mode_t); static bool check_data_dir(void); static bool mkdatadir(char *); static bool chklocale(const char *); --- 120,135 ---- #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); static char *get_encoding_id(char *); static char *get_short_version(void); ! static int mkdir_p(char *, mode_t); static bool check_data_dir(void); static bool mkdatadir(char *); static bool chklocale(const char *); *************** *** 151,157 **** static void setup_config(void); static void bootstrap_template1(char *); static void setup_shadow(void); ! static void get_set_pw(void); static void unlimit_systables(void); static void setup_depend(void); static void setup_sysviews(void); --- 146,152 ---- static void setup_config(void); static void bootstrap_template1(char *); static void setup_shadow(void); ! static void get_set_pwd(void); static void unlimit_systables(void); static void setup_depend(void); static void setup_sysviews(void); *************** *** 171,230 **** /* * macros for running pipes to postgres */ - #define PG_CMD_DECL char cmd[MAXPGPATH]; char ** line ; FILE * pg #define PG_CMD_DECL_NOLINE char cmd[MAXPGPATH]; FILE * pg - #define PG_CMD_OPEN \ - do {\ - pg = popen(cmd,PG_BINARY_W);\ - if (pg == NULL) \ - exit_nicely();\ - } while (0) - #define PG_CMD_CLOSE \ - do {\ - if(pclose(pg)>>8 &0xff)\ - exit_nicely();\ - } while (0) - #define PG_CMD_PUTLINE \ - do {\ - if (fputs(*line, pg) < 0) \ - exit_nicely(); \ - fflush(pg);\ - } while (0) - - ! #ifdef WIN32 ! ! /* workaround for win32 unlink bug, not using logging like in port/dirmod.c */ ! ! /* make sure we call the real unlink from MSVCRT */ ! #ifdef unlink ! #undef unlink #endif - static int - init_unlink(const char *path) - { - while (unlink(path)) - { - if (errno != EACCES) - return -1; - Sleep(100); /* ms */ - } - return 0; - } - #endif /* WIN32 */ - /* ! * 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) { --- 166,206 ---- /* * macros for running pipes to postgres */ #define PG_CMD_DECL char cmd[MAXPGPATH]; char ** line ; FILE * pg #define PG_CMD_DECL_NOLINE char cmd[MAXPGPATH]; FILE * pg + #define PG_CMD_OPEN \ + do { \ + pg = popen(cmd,PG_BINARY_W); \ + if (pg == NULL) \ + exit_nicely(); \ + } while (0) + + #define PG_CMD_CLOSE \ + do { \ + if(pclose(pg) >> 8 & 0xff) \ + exit_nicely(); \ + } while (0) ! #define PG_CMD_PUTLINE \ ! do { \ ! if (fputs(*line, pg) < 0) \ ! exit_nicely(); \ ! fflush(pg); \ ! } while (0) ! #ifndef WIN32 ! #define QUOTE_PATH "" ! #else ! #define QUOTE_PATH "\"" #endif /* ! * 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) { *************** *** 263,344 **** static bool rmtree(char *path, bool rmtopdir) { ! char filepath[MAXPGPATH]; ! DIR *dir; ! struct dirent *file; ! char **filenames; ! char **filename; ! int numnames = 0; ! struct stat statbuf; ! /* ! * we copy all the names out of the directory before we start ! * modifying it. ! * ! */ ! ! dir = opendir(path); ! if (dir == NULL) ! return false; ! ! while ((file = readdir(dir)) != NULL) ! { ! if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0) ! numnames++; ! } ! ! rewinddir(dir); ! ! filenames = xmalloc((numnames + 2) * sizeof(char *)); ! numnames = 0; ! ! while ((file = readdir(dir)) != NULL) ! { ! if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0) ! filenames[numnames++] = xstrdup(file->d_name); ! } ! ! filenames[numnames] = NULL; ! ! closedir(dir); ! ! /* now we have the names we can start removing things */ ! ! for (filename = filenames; *filename; filename++) ! { ! snprintf(filepath, MAXPGPATH, "%s/%s", path, *filename); ! ! if (stat(filepath, &statbuf) != 0) ! return false; ! ! if (S_ISDIR(statbuf.st_mode)) ! { ! /* call ourselves recursively for a directory */ ! if (!rmtree(filepath, true)) ! return false; ! } ! else ! { ! if (init_unlink(filepath) != 0) ! return false; ! } ! } ! if (rmtopdir) ! { ! if (rmdir(path) != 0) ! return false; ! } ! return true; } /* * make all paths look like unix, with forward slashes * also strip any trailing slash */ - static void canonicalise_path(char *path) { --- 239,271 ---- static bool rmtree(char *path, bool rmtopdir) { ! char buf[MAXPGPATH + 64]; ! int ret = 1; ! #ifndef WIN32 ! /* doesn't handle .* files */ ! snprintf(buf, sizeof(buf), "rm -rf '%s%s'", path, ! (rmtopdir) ? "" : "/*"); ! #else ! snprintf(buf, sizeof(buf), "rmdir /s /q \"%s\"", path); ! #endif ! if (system(buf) != 0) ! ret = 0; ! #ifdef WIN32 ! if (ret == 1 && !rmtopdir) ! ret = !mkdir(path); /* recreate, no permission issues? */ ! #endif ! return ret; } /* * make all paths look like unix, with forward slashes * also strip any trailing slash + * needed? */ static void canonicalise_path(char *path) { *************** *** 349,355 **** #ifdef WIN32 if (*p == '\\') *p = '/'; ! #endif /* WIN32 */ } if (p != path && *--p == '/') *p = '\0'; --- 276,282 ---- #ifdef WIN32 if (*p == '\\') *p = '/'; ! #endif } if (p != path && *--p == '/') *p = '\0'; *************** *** 361,367 **** * This does most of what sed was used for in the shell script, but * doesn't need any regexp stuff. */ - static char ** replace_token(char **lines, char *token, char *replacement) { --- 288,293 ---- *************** *** 372,378 **** replen, diff; - for (i = 0; lines[i]; i++) numlines++; --- 298,303 ---- *************** *** 431,439 **** char *buffer; int c; ! infile = fopen(path, "r"); ! ! if (!infile) { fprintf(stderr, "could not read %s ... ", path); exit_nicely(); --- 356,362 ---- char *buffer; int c; ! if ((infile = fopen(path, "r")) == NULL) { fprintf(stderr, "could not read %s ... ", path); exit_nicely(); *************** *** 491,498 **** FILE *out_file; char **line; ! out_file = fopen(path, PG_BINARY_W); ! if (out_file == NULL) { fprintf(stderr, "could not write %s ... ", path); exit_nicely(); --- 414,421 ---- FILE *out_file; char **line; ! ; ! if ((out_file = fopen(path, PG_BINARY_W)) == NULL) { fprintf(stderr, "could not write %s ... ", path); exit_nicely(); *************** *** 515,523 **** * we also assume it isn't null. * */ - static int ! build_path(char *path, mode_t omode) { struct stat sb; mode_t numask, --- 438,445 ---- * we also assume it isn't null. * */ static int ! mkdir_p(char *path, mode_t omode) { struct stat sb; mode_t numask, *************** *** 532,538 **** retval = 0; #ifdef WIN32 - /* skip network and drive specifiers for win32 */ if (strlen(p) >= 2) { --- 454,459 ---- *************** *** 551,557 **** p += 2; } } ! #endif /* WIN32 */ if (p[0] == '/') /* Skip leading '/'. */ ++p; --- 472,478 ---- p += 2; } } ! #endif if (p[0] == '/') /* Skip leading '/'. */ ++p; *************** *** 614,620 **** } if (!first && !last) (void) umask(oumask); ! return (retval); } /* --- 535,541 ---- } if (!first && !last) (void) umask(oumask); ! return retval; } /* *************** *** 673,679 **** progname); exit(1); } ! #endif /* __BEOS__ */ #else /* the windows code */ --- 594,600 ---- progname); exit(1); } ! #endif #else /* the windows code */ *************** *** 687,693 **** pw->pw_uid = 1; GetUserName(pw->pw_name, &pwname_size); ! #endif /* ! WIN32 */ return xstrdup(pw->pw_name); } --- 608,614 ---- pw->pw_uid = 1; GetUserName(pw->pw_name, &pwname_size); ! #endif return xstrdup(pw->pw_name); } *************** *** 785,791 **** closedir(chkdir); ! return (empty); } /* --- 706,712 ---- closedir(chkdir); ! return empty; } /* *************** *** 812,818 **** else if (subdir == NULL || errno != ENOENT) return false; else ! return !build_path(path, 0700); } --- 733,739 ---- else if (subdir == NULL || errno != ENOENT) return false; else ! return !mkdir_p(path, 0700); } *************** *** 853,859 **** * don't overkill * */ - #define FIND_SUCCESS 0 #define FIND_NOT_FOUND 1 #define FIND_STAT_ERR 2 --- 774,779 ---- *************** *** 875,885 **** char line[100]; #ifndef WIN32 - int permmask = S_IROTH | S_IXOTH; #endif - struct stat statbuf; FILE *pgver; int plen = strlen(path); --- 795,803 ---- *************** *** 900,911 **** return FIND_NOT_REGFILE; #ifndef WIN32 ! ! /* on windows a .exe file should be executable - this is the unix test */ ! if ((statbuf.st_mode & permmask) != permmask) return FIND_BAD_PERM; ! #endif /* ! WIN32 */ snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -V 2>%s", path, DEVNULL); --- 818,830 ---- return FIND_NOT_REGFILE; #ifndef WIN32 ! /* ! * Only unix requires this test, on WIN32 an .exe file should be ! * executable ! */ if ((statbuf.st_mode & permmask) != permmask) return FIND_BAD_PERM; ! #endif snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -V 2>%s", path, DEVNULL); *************** *** 913,923 **** return FIND_EXEC_ERR; if (fgets(line, sizeof(line), pgver) == NULL) - { perror("fgets failure"); - } - pclose(pgver); if (strcmp(line, PG_VERSIONSTR) != 0) --- 832,839 ---- *************** *** 926,939 **** return FIND_SUCCESS; } - #ifdef WIN32 - /* * 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 * */ ! static char * expanded_path(char *path) { --- 842,853 ---- return FIND_SUCCESS; } /* * 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 * expanded_path(char *path) { *************** *** 947,953 **** canonicalise_path(abspath); return xstrdup(abspath); } ! #endif /* WIN32 */ /* * set the paths pointing to postgres --- 861,867 ---- canonicalise_path(abspath); return xstrdup(abspath); } ! #endif /* * set the paths pointing to postgres *************** *** 1078,1140 **** } /* ! * 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=5 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); int i, status; for (i = 0; i < len; i++) { ! snprintf(cmd, sizeof(cmd), format, pgpath, bufs[i], DEVNULL, DEVNULL); status = system(cmd); if (status == 0) break; } if (i >= len) i = len - 1; ! n_buffers = bufs[i]; ! printf("buffers set to %d\n", n_buffers); } /* ! * 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); int i, status; for (i = 0; i < len; i++) { ! snprintf(cmd, sizeof(cmd), format, ! pgpath, n_buffers, conns[i], DEVNULL, DEVNULL); status = system(cmd); if (status == 0) break; } if (i >= len) i = len - 1; ! n_connections = conns[i]; ! printf("connections set to %d\n", n_connections); } /* --- 992,1054 ---- } /* ! * 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); int i, status; for (i = 0; i < len; i++) { ! snprintf(cmd, sizeof(cmd), format, ! pgpath, conns[i] * 5, conns[i], DEVNULL, DEVNULL); status = system(cmd); if (status == 0) break; } if (i >= len) i = len - 1; ! n_connections = conns[i]; ! printf("connections set to %d\n", n_connections); } /* ! * 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); int i, status; for (i = 0; i < len; i++) { ! snprintf(cmd, sizeof(cmd), format, pgpath, bufs[i], n_connections, ! DEVNULL, DEVNULL); status = system(cmd); if (status == 0) break; } if (i >= len) i = len - 1; ! n_buffers = bufs[i]; ! printf("buffers set to %d\n", n_buffers); } /* *************** *** 1155,1166 **** conflines = readfile(conf_file); - snprintf(repltok, sizeof(repltok), "shared_buffers = %d", n_buffers); - conflines = replace_token(conflines, "#shared_buffers = 1000", repltok); - snprintf(repltok, sizeof(repltok), "max_connections = %d", n_connections); conflines = replace_token(conflines, "#max_connections = 100", repltok); snprintf(repltok, sizeof(repltok), "lc_messages = '%s'", lc_messages); conflines = replace_token(conflines, "#lc_messages = 'C'", repltok); --- 1069,1080 ---- conflines = readfile(conf_file); snprintf(repltok, sizeof(repltok), "max_connections = %d", n_connections); conflines = replace_token(conflines, "#max_connections = 100", repltok); + snprintf(repltok, sizeof(repltok), "shared_buffers = %d", n_buffers); + conflines = replace_token(conflines, "#shared_buffers = 1000", repltok); + snprintf(repltok, sizeof(repltok), "lc_messages = '%s'", lc_messages); conflines = replace_token(conflines, "#lc_messages = 'C'", repltok); *************** *** 1190,1196 **** conflines = replace_token(conflines, "host all all ::1", "#host all all ::1"); ! #endif /* ! HAVE_IPV6 */ snprintf(path, MAXPGPATH, "%s/pg_hba.conf", pg_data); --- 1104,1110 ---- conflines = replace_token(conflines, "host all all ::1", "#host all all ::1"); ! #endif snprintf(path, MAXPGPATH, "%s/pg_hba.conf", pg_data); *************** *** 1211,1217 **** free(conflines); check_ok(); - } --- 1125,1130 ---- *************** *** 1258,1264 **** * already called setlocale(). * */ - snprintf(cmd, MAXPGPATH, "LC_COLLATE=%s", lc_collate); putenv(xstrdup(cmd)); --- 1171,1176 ---- *************** *** 1337,1359 **** * */ static void ! get_set_pw(void) { PG_CMD_DECL_NOLINE; ! char *pw1, ! *pw2; ! char pwpath[MAXPGPATH]; struct stat statbuf; ! pw1 = simple_prompt("Enter new superuser password: ", 100, false); ! pw2 = simple_prompt("Enter it again: ", 100, false); ! if (strcmp(pw1, pw2) != 0) { fprintf(stderr, "Passwords didn't match.\n"); exit_nicely(); } ! free(pw2); printf("storing the password ... "); --- 1249,1271 ---- * */ static void ! get_set_pwd(void) { PG_CMD_DECL_NOLINE; ! char *pwd1, ! *pwd2; ! char pwdpath[MAXPGPATH]; struct stat statbuf; ! pwd1 = simple_prompt("Enter new superuser password: ", 100, false); ! pwd2 = simple_prompt("Enter it again: ", 100, false); ! if (strcmp(pwd1, pwd2) != 0) { fprintf(stderr, "Passwords didn't match.\n"); exit_nicely(); } ! free(pwd2); printf("storing the password ... "); *************** *** 1365,1371 **** PG_CMD_OPEN; if (fprintf( ! pg, "ALTER USER \"%s\" WITH PASSWORD '%s';\n", username, pw1) < 0) { /* write failure */ exit_nicely(); --- 1277,1283 ---- PG_CMD_OPEN; if (fprintf( ! pg, "ALTER USER \"%s\" WITH PASSWORD '%s';\n", username, pwd1) < 0) { /* write failure */ exit_nicely(); *************** *** 1374,1381 **** PG_CMD_CLOSE; ! snprintf(pwpath, MAXPGPATH, "%s/global/pg_pwd", pg_data); ! if (stat(pwpath, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) { fprintf(stderr, "%s: The password file was not generated - " --- 1286,1293 ---- PG_CMD_CLOSE; ! snprintf(pwdpath, MAXPGPATH, "%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 - " *************** *** 1898,1904 **** * So this will need some testing on Windows. * */ - static void trapsig(int signum) { --- 1810,1815 ---- *************** *** 2009,2015 **** /* when not available, get the CTYPE setting */ lc_messages = xstrdup(setlocale(LC_CTYPE, NULL)); } ! #endif /* LC_MESSAGES */ } --- 1920,1926 ---- /* when not available, get the CTYPE setting */ lc_messages = xstrdup(setlocale(LC_CTYPE, NULL)); } ! #endif } *************** *** 2017,2023 **** * help text data * */ - char *usage_text[] = { "$CMDNAME initializes a PostgreSQL database cluster.\n", "\n", --- 1928,1933 ---- *************** *** 2117,2123 **** #if defined(__CYGWIN__) || defined(WIN32) char *exe; /* location of exe suffix in progname */ ! #endif /* defined(__CYGWIN__) || defined(WIN32) */ setlocale(LC_ALL, ""); --- 2027,2033 ---- #if defined(__CYGWIN__) || defined(WIN32) char *exe; /* location of exe suffix in progname */ ! #endif setlocale(LC_ALL, ""); *************** *** 2135,2141 **** /* strip .exe suffix, regardless of case */ *exe = '\0'; } ! #endif /* defined(__CYGWIN__) || defined(WIN32) */ if (lastsep) { --- 2045,2051 ---- /* strip .exe suffix, regardless of case */ *exe = '\0'; } ! #endif if (lastsep) { *************** *** 2317,2323 **** set_input(&features_file, "sql_features.txt"); set_input(&system_views_file, "system_views.sql"); - if (show_setting || debug) { fprintf(stderr, --- 2227,2232 ---- *************** *** 2335,2345 **** hba_file, ident_file); } - if (show_setting) exit(0); - check_input(bki_file); check_input(desc_file); check_input(hba_file); --- 2244,2252 ---- *************** *** 2388,2404 **** /* some of these are not valid on Windows */ #ifdef SIGHUP pqsignal(SIGHUP, trapsig); ! #endif /* SIGHUP */ #ifdef SIGINT pqsignal(SIGINT, trapsig); ! #endif /* SIGINT */ #ifdef SIGQUIT pqsignal(SIGQUIT, trapsig); ! #endif /* SIGQUIT */ #ifdef SIGTERM pqsignal(SIGTERM, trapsig); ! #endif /* SIGTERM */ ! /* clear this we'll use it in a few lines */ errno = 0; --- 2295,2310 ---- /* some of these are not valid on Windows */ #ifdef SIGHUP pqsignal(SIGHUP, trapsig); ! #endif #ifdef SIGINT pqsignal(SIGINT, trapsig); ! #endif #ifdef SIGQUIT pqsignal(SIGQUIT, trapsig); ! #endif #ifdef SIGTERM pqsignal(SIGTERM, trapsig); ! #endif /* clear this we'll use it in a few lines */ errno = 0; *************** *** 2420,2426 **** * check_data_dir() called opendir - the errno should still be hanging * around */ - if (errno == ENOENT) { printf("creating directory \"%s\" ... ", pg_data); --- 2326,2331 ---- *************** *** 2447,2455 **** set_null_conf(); ! test_buffers(); ! test_connections(); setup_config(); --- 2352,2360 ---- set_null_conf(); ! /* test connections first because it has more constraints */ test_connections(); + test_buffers(); setup_config(); *************** *** 2458,2466 **** set_short_version(short_version, "base/1"); setup_shadow(); - if (pwprompt) ! get_set_pw(); unlimit_systables(); --- 2363,2370 ---- set_short_version(short_version, "base/1"); setup_shadow(); if (pwprompt) ! get_set_pwd(); unlimit_systables(); *************** *** 2481,2493 **** make_template0(); printf("\n" ! "Success. You can now start the database server using:\n" ! "\n" ! " \"%s/postmaster\" -D \"%s\"\n" "or\n" ! " \"%s/pg_ctl\" -D \"%s\" -l logfile start\n" ! "\n", ! pgpath, pg_data, pgpath, pg_data); return 0; } --- 2385,2396 ---- make_template0(); printf("\n" ! "Success. You can now start the database server using:\n\n" ! " %s/postmaster -D %s%s%s\n" "or\n" ! " %s/pg_ctl -D %s%s%s -l logfile start\n\n", ! pgpath, QUOTE_PATH, pg_data, QUOTE_PATH, ! pgpath, QUOTE_PATH, pg_data, QUOTE_PATH); return 0; }
Bruce Momjian <pgman@candle.pha.pa.us> writes: > It passes all the regression tests. I have also included a diff against > Andrew's version so you can see my changes. It seems Andrew had a very > current version of initdb. The only update he missed was the change to > test the number of connections before shared buffers --- I made that > change myself. I had some concern about whether Andrew's rewrite had tracked all the recent changes to the initdb shell-script sources. Are you both confident that it is up to date? regards, tom lane
Tom Lane wrote: >Bruce Momjian <pgman@candle.pha.pa.us> writes: > > >>It passes all the regression tests. I have also included a diff against >>Andrew's version so you can see my changes. It seems Andrew had a very >>current version of initdb. The only update he missed was the change to >>test the number of connections before shared buffers --- I made that >>change myself. >> >> > >I had some concern about whether Andrew's rewrite had tracked all the >recent changes to the initdb shell-script sources. Are you both >confident that it is up to date? > > > Yes. Bruce has picked up the one change I didn't track (revision 1.204). cheers andrew
Bruce Momjian wrote: >It passes all the regression tests. I have also included a diff against >Andrew's version so you can see my changes. It seems Andrew had a very >current version of initdb. The only update he missed was the change to >test the number of connections before shared buffers --- I made that >change myself. > >I would like to apply this in the next few days to HEAD before initdb.sh >drifts. We are no longer using the WIN32_DEV branch because all our >work is now in 7.5 head. > > > I will double check this over the weekend. cheers andrew
Bruce Momjian wrote: >Here is a slightly modified version of Andrew's great work in making a C >version of initdb. Other than minor cleanups, the only big change was >to remove rmdir handling because we using rm -r and rmdir /s in >commands/dbcommands.c, so we might as use the same thing for initdb.c >rather than having code that traverses the directory tree doing 'rm'. > >The other change was to remove the unlink code and instead use >port/dirmod.c's version. > >It passes all the regression tests. I have also included a diff against >Andrew's version so you can see my changes. It seems Andrew had a very >current version of initdb. The only update he missed was the change to >test the number of connections before shared buffers --- I made that >change myself. > >I would like to apply this in the next few days to HEAD before initdb.sh >drifts. We are no longer using the WIN32_DEV branch because all our >work is now in 7.5 head. > > My comments: I have no problem with shelling out to rmdir - although my goal was to avoid shelling out to anything other than postgres itself. I think recreating the datadir if we didn't create it initially should be OK in that case, and it makes the code simpler. Not handling dot files in the Unix case should also be fine, as we know the directory was empty to start with and we don't create any. Regarding the #endif comments you removed - Peter had asked me to put them in. See http://archives.postgresql.org/pgsql-hackers/2003-10/msg00352.php You put a comment on canonicalise_path() asking if it is needed. The short answer is very much "yes". The Windows command processor will accept suitably quoted paths with forward slashes, but barfs badly with mixed forward and back slashes. (See recent discussion on hackers-win32).Removing the trailing slash on a path means we never get ugly double slashes. Feel free to put that info in as a comment if you think it is needed. The changes you made for the final message are not going to work for Windows - if pgpath or pg_data contain spaces we need quotes (even on Unix, although there most people know better than to put spaces in names). Also see above about mixed slashes. Also, putting a \ before pg_data will be nasty if it starts with a drive or network specifier - or even without (you might turn a directory specifier into a network drive specifier). It's just a message, though, and we shouldn't hold up for that - we can patch it to get it right. (Getting the path and slash thing working portably was by far the biggest headache in this project - the rest was just grunt work for the most part, although I learned a few interesting things.) Otherwise I'm fine with this. cheers andrew
Andrew Dunstan wrote: > > > Tom Lane wrote: > > >Bruce Momjian <pgman@candle.pha.pa.us> writes: > > > > > >>It passes all the regression tests. I have also included a diff against > >>Andrew's version so you can see my changes. It seems Andrew had a very > >>current version of initdb. The only update he missed was the change to > >>test the number of connections before shared buffers --- I made that > >>change myself. > >> > >> > > > >I had some concern about whether Andrew's rewrite had tracked all the > >recent changes to the initdb shell-script sources. Are you both > >confident that it is up to date? > > > > > > > > Yes. Bruce has picked up the one change I didn't track (revision 1.204). Yes, I was concerned too that everything was in there. I checked the initdb.sh logs and found that the only thing not added was the checking of the max number of connections before checking the max number of buffers, which I added. The other stuff was in there. I also checked pg_id's recent changes and those were in there too. Andrew, I assume this was a new implementation of initdb, and not taken from an older initdb C implementation made by a company. This isn't really a patch but a C replacement of a critical shell script so there is reason to double-check things. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
Bruce Momjian wrote: >Yes, I was concerned too that everything was in there. I checked the >initdb.sh logs and found that the only thing not added was the checking >of the max number of connections before checking the max number of >buffers, which I added. The other stuff was in there. I also checked >pg_id's recent changes and those were in there too. > >Andrew, I assume this was a new implementation of initdb, and not taken >from an older initdb C implementation made by a company. > >This isn't really a patch but a C replacement of a critical shell >script so there is reason to double-check things. > > Yes, I worked from initdb.sh, not from any other source. It's "all my own work" :-) I think I started with 1.201 and later upgraded to 1.203. I agree it needs careful checking - the more eyeballs the better. cheers andrew
Andrew Dunstan wrote: > > > Bruce Momjian wrote: > > >Here is a slightly modified version of Andrew's great work in making a C > >version of initdb. Other than minor cleanups, the only big change was > >to remove rmdir handling because we using rm -r and rmdir /s in > >commands/dbcommands.c, so we might as use the same thing for initdb.c > >rather than having code that traverses the directory tree doing 'rm'. > > > >The other change was to remove the unlink code and instead use > >port/dirmod.c's version. > > > >It passes all the regression tests. I have also included a diff against > >Andrew's version so you can see my changes. It seems Andrew had a very > >current version of initdb. The only update he missed was the change to > >test the number of connections before shared buffers --- I made that > >change myself. > > > >I would like to apply this in the next few days to HEAD before initdb.sh > >drifts. We are no longer using the WIN32_DEV branch because all our > >work is now in 7.5 head. > > > > > > My comments: > > I have no problem with shelling out to rmdir - although my goal was to > avoid shelling out to anything other than postgres itself. I think > recreating the datadir if we didn't create it initially should be OK in > that case, and it makes the code simpler. Not handling dot files in the > Unix case should also be fine, as we know the directory was empty to > start with and we don't create any. Good. I can do rmdir() in C in port/dirmod.c if we need it. Right now we are doing system(rm/rmdir) in dbcommands.c so we should consistent. Let's stay with system(rm/rmdir) and if it doesn't work as we expect, we can add your rmdir() code and put it in port/dirmod.c. > Regarding the #endif comments you removed - Peter had asked me to put > them in. See > http://archives.postgresql.org/pgsql-hackers/2003-10/msg00352.php Peter, I thought we add: #endif /* port... */ only when the define covers many lines of code. This case: #ifdef WIN32 some code #endif seems clearer than: #ifdef WIN32 some code #endif /* WIN32 */ Of course, if the code block is very large, a closing comment can sometimes help. Personally, I don't like the comments anytime, but if people like them on long blocks, I can understand that. > You put a comment on canonicalise_path() asking if it is needed. The > short answer is very much "yes". The Windows command processor will > accept suitably quoted paths with forward slashes, but barfs badly with > mixed forward and back slashes. (See recent discussion on > hackers-win32).Removing the trailing slash on a path means we never get > ugly double slashes. Feel free to put that info in as a comment if you > think it is needed. Done. > The changes you made for the final message are not going to work for > Windows - if pgpath or pg_data contain spaces we need quotes (even on > Unix, although there most people know better than to put spaces in > names). Also see above about mixed slashes. Also, putting a \ before > pg_data will be nasty if it starts with a drive or network specifier - > or even without (you might turn a directory specifier into a network > drive specifier). It's just a message, though, and we shouldn't hold up > for that - we can patch it to get it right. I am confused. The only change I made was to have the quotes appear only on WIN32. #ifndef WIN32 #define QUOTE_PATH "" #else #define QUOTE_PATH "\"" #endif ... printf("\n" "Success. You can now start the database server using:\n\n" " %s/postmaster -D %s%s%s\n" "or\n" " %s/pg_ctl -D %s%s%s -l logfile start\n\n", pgpath, QUOTE_PATH, pg_data, QUOTE_PATH, pgpath, QUOTE_PATH, pg_data, QUOTE_PATH); (I also merged multiple \n into a single line.) My logic was that spaces in directory names are much more common on WIN32, so we should display the quotes only on WIN32. Perhaps you read "\"" as "\\"? > (Getting the path and slash thing working portably was by far the > biggest headache in this project - the rest was just grunt work for the > most part, although I learned a few interesting things.) > > Otherwise I'm fine with this. Thanks for reviewing my work. I am surprised how small the new initdb.c is. It is 50k vs 38k for initdb.sh. Of course, system_views.sql is another 10k, but still, it is smaller than I thought, and quite clear. Thanks. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
Andrew Dunstan wrote: > > > Bruce Momjian wrote: > > >Yes, I was concerned too that everything was in there. I checked the > >initdb.sh logs and found that the only thing not added was the checking > >of the max number of connections before checking the max number of > >buffers, which I added. The other stuff was in there. I also checked > >pg_id's recent changes and those were in there too. > > > >Andrew, I assume this was a new implementation of initdb, and not taken > >from an older initdb C implementation made by a company. > > > >This isn't really a patch but a C replacement of a critical shell > >script so there is reason to double-check things. > > > > > > > Yes, I worked from initdb.sh, not from any other source. It's "all my > own work" :-) I think I started with 1.201 and later upgraded to 1.203. > > I agree it needs careful checking - the more eyeballs the better. The great part is that it look so much like our code, unlike the commerical port code I have seen for initdb in the past. This certainly moves us forward on Win32. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
Andrew Dunstan writes: > recreating the datadir if we didn't create it initially should be OK in > that case, and it makes the code simpler. That should be avoided, because you'll have trouble recreating the original directory with all its properties such as ownership, permissions, etc., at least not without making the code anything but simpler. There might even be a situation were you are allowed to delete the directory but cannot create a new one. -- Peter Eisentraut peter_e@gmx.net
Peter Eisentraut <peter_e@gmx.net> writes: > Andrew Dunstan writes: >> recreating the datadir if we didn't create it initially should be OK in >> that case, and it makes the code simpler. > That should be avoided, because you'll have trouble recreating the > original directory with all its properties such as ownership, permissions, > etc., at least not without making the code anything but simpler. There > might even be a situation were you are allowed to delete the directory but > cannot create a new one. Consider also the strong likelihood that the data directory's parent directory is owned by root, so that you do not have the ability to delete and recreate the data directory because you don't have write permission on its parent. The main reason initdb is set up to be able to start with an existing-but-empty data dir is exactly because creating that directory may have required permissions that initdb itself hasn't got. regards, tom lane
Bruce Momjian wrote: >Andrew Dunstan wrote: > > >> >> >>My comments: >> >>I have no problem with shelling out to rmdir - although my goal was to >>avoid shelling out to anything other than postgres itself. I think >>recreating the datadir if we didn't create it initially should be OK in >>that case, and it makes the code simpler. Not handling dot files in the >>Unix case should also be fine, as we know the directory was empty to >>start with and we don't create any. >> >> > >Good. I can do rmdir() in C in port/dirmod.c if we need it. Right now >we are doing system(rm/rmdir) in dbcommands.c so we should consistent. >Let's stay with system(rm/rmdir) and if it doesn't work as we expect, we >can add your rmdir() code and put it in port/dirmod.c. > > In view of Peter's email of a few minutes ago I think we need to do that. >>The changes you made for the final message are not going to work for >>Windows - if pgpath or pg_data contain spaces we need quotes (even on >>Unix, although there most people know better than to put spaces in >>names). Also see above about mixed slashes. Also, putting a \ before >>pg_data will be nasty if it starts with a drive or network specifier - >>or even without (you might turn a directory specifier into a network >>drive specifier). It's just a message, though, and we shouldn't hold up >>for that - we can patch it to get it right. >> >> > >I am confused. The only change I made was to have the quotes appear >only on WIN32. > > #ifndef WIN32 > #define QUOTE_PATH "" > #else > #define QUOTE_PATH "\"" > #endif > > ... > > printf("\n" > "Success. You can now start the database server using:\n\n" > " %s/postmaster -D %s%s%s\n" > "or\n" > " %s/pg_ctl -D %s%s%s -l logfile start\n\n", > pgpath, QUOTE_PATH, pg_data, QUOTE_PATH, > pgpath, QUOTE_PATH, pg_data, QUOTE_PATH); > >(I also merged multiple \n into a single line.) My logic was that >spaces in directory names are much more common on WIN32, so we should >display the quotes only on WIN32. Perhaps you read "\"" as "\\"? > > Yes, I did. Sorry about that. But we also need to quote the path (the most obvious place to put it after all is "C:\Program Files\PostgreSQL"). In fact, that's more important than the data location. Unfortunately, the Windows command processor barfs on multiple quotes strings ;-( cheers andrew
Peter Eisentraut wrote: > Andrew Dunstan writes: > > > recreating the datadir if we didn't create it initially should be OK in > > that case, and it makes the code simpler. > > That should be avoided, because you'll have trouble recreating the > original directory with all its properties such as ownership, permissions, > etc., at least not without making the code anything but simpler. There > might even be a situation were you are allowed to delete the directory but > cannot create a new one. Recreating the directory only happens on WIN32, where rmdir doesn't allow you to only delete files and subdirectories and not the parent directory. Non-Win32 does rm -rf dir/*. Is that OK? -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
Tom Lane wrote: > Peter Eisentraut <peter_e@gmx.net> writes: > > Andrew Dunstan writes: > >> recreating the datadir if we didn't create it initially should be OK in > >> that case, and it makes the code simpler. > > > That should be avoided, because you'll have trouble recreating the > > original directory with all its properties such as ownership, permissions, > > etc., at least not without making the code anything but simpler. There > > might even be a situation were you are allowed to delete the directory but > > cannot create a new one. > > Consider also the strong likelihood that the data directory's parent > directory is owned by root, so that you do not have the ability to > delete and recreate the data directory because you don't have write > permission on its parent. The main reason initdb is set up to be able > to start with an existing-but-empty data dir is exactly because creating > that directory may have required permissions that initdb itself hasn't > got. Again, this directory recreate happens only on Win32, an I thought it would be OK there. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
Bruce Momjian <pgman@candle.pha.pa.us> writes: >> Consider also the strong likelihood that the data directory's parent >> directory is owned by root, > Again, this directory recreate happens only on Win32, an I thought it > would be OK there. Windows has no concept of directory permissions at all? I thought the more recent versions had at least rudimentary permissions. regards, tom lane
Bruce Momjian <pgman@candle.pha.pa.us> writes: > Recreating the directory only happens on WIN32, where rmdir doesn't > allow you to only delete files and subdirectories and not the parent > directory. Non-Win32 does rm -rf dir/*. I think we should forget about invoking rm as a subprocess at all, and just do the recursive directory walk and unlinks for ourselves. We already have code to do this for copy in copydir.c, and unlink would not be any longer. We will probably be forced into implementing database removal for ourselves rather than by 'rm' hacks anyway as soon as tablespaces come to pass; so why contort initdb's behavior for a very transient implementation savings? regards, tom lane
Andrew Dunstan wrote: > >Good. I can do rmdir() in C in port/dirmod.c if we need it. Right now > >we are doing system(rm/rmdir) in dbcommands.c so we should consistent. > >Let's stay with system(rm/rmdir) and if it doesn't work as we expect, we > >can add your rmdir() code and put it in port/dirmod.c. > > > > > > In view of Peter's email of a few minutes ago I think we need to do that. Again, recreate is only Win32. I just figured out how to do this on Win32. We have to use 'del' rather than 'rmdir' to keep the directory: New code is: /* * delete a directory tree recursively * 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) { char buf[MAXPGPATH + 64]; #ifndef WIN32 /* doesn't handle .* files */ snprintf(buf, sizeof(buf), "rm -rf '%s%s'", path, rmtopdir ? "" : "/*"); #else snprintf(buf, sizeof(buf), "%s /s /q \"%s\"", rmtopdir ? "rmdir" : "del", path); #endif return !system(buf); } > > printf("\n" > > "Success. You can now start the database server using:\n\n" > > " %s/postmaster -D %s%s%s\n" > > "or\n" > > " %s/pg_ctl -D %s%s%s -l logfile start\n\n", > > pgpath, QUOTE_PATH, pg_data, QUOTE_PATH, > > pgpath, QUOTE_PATH, pg_data, QUOTE_PATH); > > > >(I also merged multiple \n into a single line.) My logic was that > >spaces in directory names are much more common on WIN32, so we should > >display the quotes only on WIN32. Perhaps you read "\"" as "\\"? > > > > > > Yes, I did. Sorry about that. But we also need to quote the path (the > most obvious place to put it after all is "C:\Program > Files\PostgreSQL"). In fact, that's more important than the data > location. Unfortunately, the Windows command processor barfs on multiple > quotes strings ;-( I ran some tests using XP "CMD" and found: "C:\test" and "\test" works but: "test" does not work. Since I see that the output always has a leading path, I have modified the code to do: printf("\nSuccess. You can now start the database server using:\n\n" " %s%s%s/postmaster -D %s%s%s\n" "or\n" " %s%s%s/pg_ctl -D %s%s%s -l logfile start\n\n", QUOTE_PATH, pgpath, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH, QUOTE_PATH, pgpath, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH); I am attaching the updated initdb.c file. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073 /*------------------------------------------------------------------------- * * initdb * * author: Andrew Dunstan mailto:andrew@dunslane.net * * Copyright (C) 2003 Andrew Dunstan * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * This code is released under the terms of the PostgreSQL License. * * This is a C implementation of the previous shell script for setting up a * PostgreSQL cluster location, and should be highly compatible with it. * * $Header$ * * 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? * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "getopt_long.h" #include <dirent.h> #include <sys/stat.h> #include <unistd.h> #include <locale.h> #include <signal.h> #include "libpq/pqsignal.h" #include "mb/pg_wchar.h" /* version string we expect back from postgres */ #define PG_VERSIONSTR "postgres (PostgreSQL) " PG_VERSION "\n" /* values passed in by makefile define */ char *bindir = PGBINDIR; char *datadir = PGDATADIR; /* values to be obtained from arguments */ char *pg_data = ""; char *encoding = ""; char *locale = ""; char *lc_collate = ""; char *lc_ctype = ""; char *lc_monetary = ""; char *lc_numeric = ""; char *lc_time = ""; char *lc_messages = ""; char *username = ""; bool pwprompt = false; bool debug = false; bool noclean = false; bool show_help = false; bool show_version = false; bool show_setting = false; /* internal vars */ char *progname; char *self_path; char *postgres; char *encodingid = "0"; char *bki_file; char *desc_file; char *hba_file; char *ident_file; char *conf_file; char *conversion_file; char *info_schema_file; char *features_file; char *system_views_file; char *effective_user; bool testpath = true; bool made_new_pgdata = false; char infoversion[100]; bool not_ok = false; /* defaults */ int n_connections = 10; int n_buffers = 50; /* platform specific path stuff */ #if defined(__CYGWIN__) || defined(WIN32) #define EXE ".exe" #define DEVNULL "nul" #else #define EXE "" #define DEVNULL "/dev/null" #endif #ifdef WIN32 #define PATHSEP ';' #else #define PATHSEP ':' #endif /* detected path to postgres and (we assume) friends */ char *pgpath; /* forward declare all our functions */ 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); static char *get_encoding_id(char *); static char *get_short_version(void); static int mkdir_p(char *, mode_t); static bool check_data_dir(void); static bool mkdatadir(char *); static bool chklocale(const char *); static void setlocales(void); static void set_input(char **, char *); static void check_input(char *path); static int find_postgres(char *); static int set_paths(void); static char **replace_token(char **, char *, char *); static void set_short_version(char *, char *); static void set_null_conf(void); static void test_buffers(void); static void test_connections(void); static void setup_config(void); static void bootstrap_template1(char *); static void setup_shadow(void); static void get_set_pwd(void); static void unlimit_systables(void); static void setup_depend(void); static void setup_sysviews(void); static void setup_description(void); static void setup_conversion(void); static void setup_privileges(void); static void set_info_version(void); static void setup_schema(void); static void vacuum_db(void); static void make_template0(void); static void usage(void); static void trapsig(int); static void check_ok(void); static char *xstrdup(const char *); static void *xmalloc(size_t); /* * macros for running pipes to postgres */ #define PG_CMD_DECL char cmd[MAXPGPATH]; char ** line ; FILE * pg #define PG_CMD_DECL_NOLINE char cmd[MAXPGPATH]; FILE * pg #define PG_CMD_OPEN \ do { \ pg = popen(cmd,PG_BINARY_W); \ if (pg == NULL) \ exit_nicely(); \ } while (0) #define PG_CMD_CLOSE \ do { \ if(pclose(pg) >> 8 & 0xff) \ exit_nicely(); \ } while (0) #define PG_CMD_PUTLINE \ do { \ if (fputs(*line, pg) < 0) \ exit_nicely(); \ fflush(pg); \ } while (0) #ifndef WIN32 #define QUOTE_PATH "" #else #define QUOTE_PATH "\"" #endif /* * 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) { void *result; result = malloc(size); if (!result) { fputs("malloc failure - bailing out\n", stderr); exit(1); } return result; } static char * xstrdup(const char *s) { char *result; result = strdup(s); if (!result) { fputs("strdup failure - bailing out\n", stderr); exit(1); } return result; } /* * delete a directory tree recursively * 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) { char buf[MAXPGPATH + 64]; #ifndef WIN32 /* doesn't handle .* files */ snprintf(buf, sizeof(buf), "rm -rf '%s%s'", path, rmtopdir ? "" : "/*"); #else snprintf(buf, sizeof(buf), "%s /s /q \"%s\"", rmtopdir ? "rmdir" : "del", path); #endif return !system(buf); } /* * 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. */ static void canonicalize_path(char *path) { char *p; for (p = path; *p; p++) { #ifdef WIN32 if (*p == '\\') *p = '/'; #endif } if (p != path && *--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. */ static char ** replace_token(char **lines, char *token, char *replacement) { int numlines = 1; int i; char **result; int toklen, replen, diff; for (i = 0; lines[i]; i++) numlines++; result = (char **) xmalloc(numlines * sizeof(char *)); toklen = strlen(token); replen = strlen(replacement); diff = replen - toklen; for (i = 0; i < numlines; i++) { char *where; char *newline; int pre; /* just copy pointer if NULL or no change needed */ if (lines[i] == NULL || (where = strstr(lines[i], token)) == NULL) { result[i] = lines[i]; continue; } /* if we get here a change is needed - set up new line */ newline = (char *) xmalloc(strlen(lines[i]) + diff + 1); pre = where - lines[i]; strncpy(newline, lines[i], pre); strcpy(newline + pre, replacement); strcpy(newline + pre + replen, lines[i] + pre + toklen); result[i] = newline; } return result; } /* * get the lines from a text file * */ static char ** readfile(char *path) { FILE *infile; int maxlength = 0, linelen = 0; int nlines = 0; char **result; char *buffer; int c; if ((infile = fopen(path, "r")) == NULL) { fprintf(stderr, "could not read %s ... ", path); exit_nicely(); } /* pass over the file twice - the first time to size the result */ while ((c = fgetc(infile)) != EOF) { linelen++; if (c == '\n') { nlines++; if (linelen > maxlength) maxlength = linelen; linelen = 0; } } /* handle last line without a terminating newline (yuck) */ if (linelen) nlines++; if (linelen > maxlength) maxlength = linelen; /* set up the result and the line buffer */ result = (char **) xmalloc((nlines + 2) * sizeof(char *)); buffer = (char *) xmalloc(maxlength + 2); /* now reprocess the file and store the lines */ rewind(infile); nlines = 0; while (fgets(buffer, maxlength + 1, infile) != NULL) { result[nlines] = xstrdup(buffer); nlines++; } fclose(infile); result[nlines] = NULL; return result; } /* * write an array of lines to a file * */ static void writefile(char *path, char **lines) { FILE *out_file; char **line; ; if ((out_file = fopen(path, PG_BINARY_W)) == NULL) { fprintf(stderr, "could not write %s ... ", path); exit_nicely(); } for (line = lines; *line != NULL; line++) { if (fputs(*line, out_file) < 0) exit_nicely(); free(*line); } if (fclose(out_file)) exit_nicely(); } /* source stolen from FreeBSD /src/bin/mkdir/mkdir.c and adapted */ /* * 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) { struct stat sb; mode_t numask, oumask; int first, last, retval; char *p; p = path; oumask = 0; retval = 0; #ifdef WIN32 /* skip network and drive specifiers for win32 */ if (strlen(p) >= 2) { if (p[0] == '/' && p[1] == '/') { /* network drive */ p = strstr(p + 2, "/"); if (p == NULL) return 1; } else if (p[1] == ':' && ((p[0] >= 'a' && p[0] <= 'z') || (p[0] >= 'A' && p[0] <= 'Z'))) { /* local drive */ p += 2; } } #endif if (p[0] == '/') /* Skip leading '/'. */ ++p; for (first = 1, last = 0; !last; ++p) { if (p[0] == '\0') last = 1; else if (p[0] != '/') continue; *p = '\0'; if (p[1] == '\0') last = 1; if (first) { /* * POSIX 1003.2: For each dir operand that does not name an * existing directory, effects equivalent to those cased by * the following command shall occcur: * * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] * dir * * We change the user's umask and then restore it, instead of * doing chmod's. */ oumask = umask(0); numask = oumask & ~(S_IWUSR | S_IXUSR); (void) umask(numask); first = 0; } if (last) (void) umask(oumask); if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) { if (errno == EEXIST || errno == EISDIR) { if (stat(path, &sb) < 0) { retval = 1; break; } else if (!S_ISDIR(sb.st_mode)) { if (last) errno = EEXIST; else errno = ENOTDIR; retval = 1; break; } } else { retval = 1; break; } } if (!last) *p = '/'; } if (!first && !last) (void) umask(oumask); return retval; } /* * clean up any files we created on failure * if we created the data directory remove it too * */ static void exit_nicely(void) { fprintf(stderr, "%s: failed\n", progname); if (!noclean) { if (made_new_pgdata) { fprintf(stderr, "%s: removing data directory \"%s\"\n", progname, pg_data); if (!rmtree(pg_data, true)) fprintf(stderr, "%s: failed\n", progname); } else { fprintf(stderr, "%s: removing contents of data directory \"%s\"\n", progname, pg_data); if (!rmtree(pg_data, false)) fprintf(stderr, "%s: failed\n", progname); } } exit(1); } /* * find the current user using code lifted from pg_id.c * on unix make sure it isn't really root * */ static char * get_id(void) { #ifndef WIN32 struct passwd *pw; pw = getpwuid(getuid()); #ifndef __BEOS__ /* no root check on BEOS */ if (!geteuid()) /* 0 is root's uid */ { 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", progname); exit(1); } #endif #else /* the windows code */ struct passwd_win32 { int pw_uid; char pw_name[128]; } pass_win32; struct passwd_win32 *pw = &pass_win32; DWORD pwname_size = sizeof(pass_win32.pw_name) - 1; pw->pw_uid = 1; GetUserName(pw->pw_name, &pwname_size); #endif return xstrdup(pw->pw_name); } /* * get the encoding id for a given encoding name * */ static char * get_encoding_id(char *encoding_name) { int enc; char result[20]; if (encoding_name && *encoding_name) { if ((enc = pg_char_to_encoding(encoding_name)) >= 0 && pg_valid_server_encoding(encoding_name) >= 0) { sprintf(result, "%d", enc); return xstrdup(result); } } fprintf(stderr, "%s: \"%s\" is not a valid server encoding name\n", progname, encoding_name ? encoding_name : "(null)"); exit(1); } /* * get short version of VERSION * */ static char * get_short_version(void) { bool gotdot = false; int end; char *vr; vr = xstrdup(PG_VERSION); for (end = 0; vr[end] != '\0'; end++) { if (vr[end] == '.') { if (end == 0) return NULL; else if (gotdot) break; else gotdot = true; } else if (vr[end] < '0' || vr[end] > '9') { /* gone past digits and dots */ break; } } if (end == 0 || vr[end - 1] == '.' || !gotdot) return NULL; vr[end] = '\0'; return vr; } /* * make sure the data directory either doesn't exist or is empty * */ static bool check_data_dir(void) { DIR *chkdir; struct dirent *file; bool empty = true; chkdir = opendir(pg_data); if (!chkdir) return (errno == ENOENT); while ((file = readdir(chkdir)) != NULL) { if (strcmp(".", file->d_name) == 0 || strcmp("..", file->d_name) == 0) { /* skip this and parent directory */ continue; } else { empty = false; break; } } closedir(chkdir); return empty; } /* * make the data directory (or one of its subdirectories if subdir is not NULL) * */ static bool mkdatadir(char *subdir) { char *path; int res; path = xmalloc(strlen(pg_data) + 2 + (subdir == NULL ? 0 : strlen(subdir))); if (subdir != NULL) sprintf(path, "%s/%s", pg_data, subdir); else strcpy(path, pg_data); res = mkdir(path, 0700); if (res == 0) return true; else if (subdir == NULL || errno != ENOENT) return false; else return !mkdir_p(path, 0700); } /* * set name of given input file variable under data directory * */ static void set_input(char **dest, char *filename) { *dest = xmalloc(strlen(datadir) + strlen(filename) + 2); sprintf(*dest, "%s/%s", datadir, filename); } /* * check that given input file exists * */ static void check_input(char *path) { struct stat statbuf; if (stat(path, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) { fprintf(stderr, "%s: file \"%s\" not found\n" "This means you have a corrupted installation or identified\n" "the wrong directory with the invocation option -L.\n", progname, path); exit(1); } } /* * TODO - clean this up and handle the errors properly * don't overkill * */ #define FIND_SUCCESS 0 #define FIND_NOT_FOUND 1 #define FIND_STAT_ERR 2 #define FIND_NOT_REGFILE 3 #define FIND_BAD_PERM 4 #define FIND_EXEC_ERR 5 #define FIND_WRONG_VERSION 6 /* * see if there is a postgres executable in the given path, and giving the * right version number * */ static int find_postgres(char *path) { char fn[MAXPGPATH]; char cmd[MAXPGPATH]; char line[100]; #ifndef WIN32 int permmask = S_IROTH | S_IXOTH; #endif struct stat statbuf; FILE *pgver; int plen = strlen(path); if (path[plen - 1] != '/') snprintf(fn, MAXPGPATH, "%s/postgres%s", path, EXE); else snprintf(fn, MAXPGPATH, "%spostgres%s", path, EXE); if (stat(fn, &statbuf) != 0) { if (errno == ENOENT) return FIND_NOT_FOUND; else return FIND_STAT_ERR; } if (!S_ISREG(statbuf.st_mode)) return FIND_NOT_REGFILE; #ifndef WIN32 /* * Only unix requires this test, on WIN32 an .exe file should be * executable */ if ((statbuf.st_mode & permmask) != permmask) return FIND_BAD_PERM; #endif snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -V 2>%s", path, DEVNULL); if ((pgver = popen(cmd, "r")) == NULL) return FIND_EXEC_ERR; if (fgets(line, sizeof(line), pgver) == NULL) perror("fgets failure"); pclose(pgver); if (strcmp(line, PG_VERSIONSTR) != 0) return FIND_WRONG_VERSION; return FIND_SUCCESS; } /* * 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 * expanded_path(char *path) { char abspath[MAXPGPATH]; if (_fullpath(abspath, path, MAXPGPATH) == NULL) { perror("expanded path"); return path; } canonicalize_path(abspath); return xstrdup(abspath); } #endif /* * 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. * */ static int set_paths(void) { if (testpath && !self_path) { char *path, *cursor; int pathlen, i, pathsegs; char **pathbits; char buf[MAXPGPATH]; struct stat statbuf; path = xstrdup(getenv("PATH")); pathlen = strlen(path); for (i = 0, pathsegs = 1; i < pathlen; i++) { if (path[i] == PATHSEP) pathsegs++; } pathbits = (char **) xmalloc(pathsegs * sizeof(char *)); for (i = 0, pathsegs = 0, cursor = path; i <= pathlen; i++) { if (path[i] == PATHSEP || path[i] == 0) { path[i] = 0; if (strlen(cursor) == 0) { /* empty path segment means current directory */ pathbits[pathsegs] = xstrdup("."); } else { canonicalize_path(cursor); pathbits[pathsegs] = cursor; } pathsegs++; cursor = path + i + 1; } } for (i = 0; i < pathsegs; i++) { snprintf(buf, MAXPGPATH, "%s/%s%s", pathbits[i], progname, EXE); if (stat(buf, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) { self_path = pathbits[i]; break; } } } if (testpath && self_path && (find_postgres(expanded_path(self_path)) == 0)) { /* we found postgres on out own path */ pgpath = expanded_path(self_path); } else { /* look in the hardcoded bindir */ int res; char *cbindir; cbindir = xstrdup(bindir); canonicalize_path(cbindir); res = find_postgres(expanded_path(cbindir)); if (res == 0) pgpath = expanded_path(cbindir); else return 1; } return 0; } /* * 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) { FILE *version_file; char *path; if (extrapath == NULL) { path = xmalloc(strlen(pg_data) + 12); sprintf(path, "%s/PG_VERSION", pg_data); } else { path = xmalloc(strlen(pg_data) + strlen(extrapath) + 13); sprintf(path, "%s/%s/PG_VERSION", pg_data, extrapath); } version_file = fopen(path, PG_BINARY_W); fprintf(version_file, "%s\n", short_version); fclose(version_file); } /* * set up an empty config file so we can check buffers and connections * */ static void set_null_conf(void) { FILE *conf_file; char *path; path = xmalloc(strlen(pg_data) + 17); sprintf(path, "%s/postgresql.conf", pg_data); conf_file = fopen(path, PG_BINARY_W); fclose(conf_file); } /* * 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); int i, status; for (i = 0; i < len; i++) { snprintf(cmd, sizeof(cmd), format, pgpath, conns[i] * 5, conns[i], DEVNULL, DEVNULL); status = system(cmd); if (status == 0) break; } if (i >= len) i = len - 1; n_connections = conns[i]; printf("connections set to %d\n", n_connections); } /* * 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); int i, status; for (i = 0; i < len; i++) { snprintf(cmd, sizeof(cmd), format, pgpath, bufs[i], n_connections, DEVNULL, DEVNULL); status = system(cmd); if (status == 0) break; } if (i >= len) i = len - 1; n_buffers = bufs[i]; printf("buffers set to %d\n", n_buffers); } /* * set up all the config files * */ static void setup_config(void) { char **conflines; char repltok[100]; char path[MAXPGPATH]; fputs("creating configuration files ... ", stdout); /* postgresql.conf */ conflines = readfile(conf_file); snprintf(repltok, sizeof(repltok), "max_connections = %d", n_connections); conflines = replace_token(conflines, "#max_connections = 100", repltok); snprintf(repltok, sizeof(repltok), "shared_buffers = %d", n_buffers); conflines = replace_token(conflines, "#shared_buffers = 1000", repltok); snprintf(repltok, sizeof(repltok), "lc_messages = '%s'", lc_messages); conflines = replace_token(conflines, "#lc_messages = 'C'", repltok); snprintf(repltok, sizeof(repltok), "lc_monetary = '%s'", lc_monetary); conflines = replace_token(conflines, "#lc_monetary = 'C'", repltok); snprintf(repltok, sizeof(repltok), "lc_numeric = '%s'", lc_numeric); conflines = replace_token(conflines, "#lc_numeric = 'C'", repltok); 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); writefile(path, conflines); chmod(path, 0600); free(conflines); /* pg_hba.conf */ conflines = readfile(hba_file); #ifndef HAVE_IPV6 conflines = replace_token(conflines, "host all all ::1", "#host all all ::1"); #endif snprintf(path, MAXPGPATH, "%s/pg_hba.conf", pg_data); writefile(path, conflines); chmod(path, 0600); free(conflines); /* pg_ident.conf */ conflines = readfile(ident_file); snprintf(path, MAXPGPATH, "%s/pg_ident.conf", pg_data); writefile(path, conflines); chmod(path, 0600); free(conflines); check_ok(); } /* * run the bootstrap code * */ static void bootstrap_template1(char *short_version) { char *talkargs = ""; char **bki_lines; char headerline[MAXPGPATH]; PG_CMD_DECL; printf("creating template1 database in %s/base/1 ... ", pg_data); if (debug) talkargs = "-d 5"; bki_lines = readfile(bki_file); snprintf(headerline, MAXPGPATH, "# PostgreSQL %s\n", short_version); if (strcmp(headerline, *bki_lines) != 0) { fprintf(stderr, "%s: input file \"%s\" does not belong to PostgreSQL %s\n" "Check your installation or specify the correct path " "using the option -L.\n", progname, bki_file, PG_VERSION); exit_nicely(); } bki_lines = replace_token(bki_lines, "POSTGRES", effective_user); 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(). * */ snprintf(cmd, MAXPGPATH, "LC_COLLATE=%s", lc_collate); putenv(xstrdup(cmd)); snprintf(cmd, MAXPGPATH, "LC_CTYPE=%s", lc_ctype); putenv(xstrdup(cmd)); putenv("LC_ALL"); snprintf(cmd, MAXPGPATH, " \"%s/postgres\" -boot -x1 -F %s template1", pgpath, talkargs); PG_CMD_OPEN; for (line = bki_lines; *line != NULL; line++) { PG_CMD_PUTLINE; free(*line); } PG_CMD_CLOSE; free(bki_lines); check_ok(); } /* * 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 * written to the flat password/group files pg_pwd and pg_group */ "CREATE TRIGGER pg_sync_pg_pwd " " AFTER INSERT OR UPDATE OR DELETE ON pg_shadow " " FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();\n", "CREATE TRIGGER pg_sync_pg_group " " AFTER INSERT OR UPDATE OR DELETE ON pg_group " " FOR EACH ROW EXECUTE PROCEDURE update_pg_pwd_and_pg_group();\n", /* * needs to be done before alter user, because alter user checks * that pg_shadow is secure ... */ "REVOKE ALL on pg_shadow FROM public;\n", NULL }; PG_CMD_DECL; fputs("initializing pg_shadow ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; for (line = pg_shadow_setup; *line != NULL; line++) PG_CMD_PUTLINE; PG_CMD_CLOSE; check_ok(); } /* * get the superuser password if required, and call postgres to set it * */ static void get_set_pwd(void) { PG_CMD_DECL_NOLINE; char *pwd1, *pwd2; char pwdpath[MAXPGPATH]; struct stat statbuf; pwd1 = simple_prompt("Enter new superuser password: ", 100, false); pwd2 = simple_prompt("Enter it again: ", 100, false); if (strcmp(pwd1, pwd2) != 0) { fprintf(stderr, "Passwords didn't match.\n"); exit_nicely(); } free(pwd2); printf("storing the password ... "); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; if (fprintf( pg, "ALTER USER \"%s\" WITH PASSWORD '%s';\n", username, pwd1) < 0) { /* write failure */ exit_nicely(); } fflush(pg); PG_CMD_CLOSE; snprintf(pwdpath, MAXPGPATH, "%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", progname); exit_nicely(); } check_ok(); } /* * toast sys tables * */ static void unlimit_systables(void) { char *systables_setup[] = { "ALTER TABLE pg_attrdef CREATE TOAST TABLE;\n", "ALTER TABLE pg_constraint CREATE TOAST TABLE;\n", "ALTER TABLE pg_database CREATE TOAST TABLE;\n", "ALTER TABLE pg_description CREATE TOAST TABLE;\n", "ALTER TABLE pg_group CREATE TOAST TABLE;\n", "ALTER TABLE pg_proc CREATE TOAST TABLE;\n", "ALTER TABLE pg_rewrite CREATE TOAST TABLE;\n", "ALTER TABLE pg_shadow CREATE TOAST TABLE;\n", "ALTER TABLE pg_statistic CREATE TOAST TABLE;\n", NULL }; PG_CMD_DECL; fputs("enabling unlimited row size for system tables ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; for (line = systables_setup; *line != NULL; line++) PG_CMD_PUTLINE; PG_CMD_CLOSE; check_ok(); } /* * set up pg_depend * */ static void setup_depend(void) { char *pg_depend_setup[] = { /* * Make PIN entries in pg_depend for all objects made so far in * 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. */ "DELETE FROM pg_depend;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_class;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_proc;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_type;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_cast;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_constraint;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_attrdef;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_language;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_operator;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_opclass;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_rewrite;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_trigger;\n", /* * restriction here to avoid pinning the public namespace */ "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_namespace " " WHERE nspname LIKE 'pg%';\n", NULL }; PG_CMD_DECL; fputs("initializing pg_depend ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; for (line = pg_depend_setup; *line != NULL; line++) PG_CMD_PUTLINE; PG_CMD_CLOSE; check_ok(); } /* * set up system views * */ static void setup_sysviews(void) { PG_CMD_DECL; char **sysviews_setup; fputs("creating system views ... ", stdout); 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); PG_CMD_OPEN; for (line = sysviews_setup; *line != NULL; line++) { PG_CMD_PUTLINE; free(*line); } PG_CMD_CLOSE; free(sysviews_setup); check_ok(); } /* * 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; fputs("loading pg_description ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, 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); for (line = pg_description_setup2; *line != NULL; line++) PG_CMD_PUTLINE; PG_CMD_CLOSE; check_ok(); } /* * load conversion functions * */ static void setup_conversion(void) { PG_CMD_DECL; char **conv_lines; fputs("creating conversions ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; conv_lines = readfile(conversion_file); for (line = conv_lines; *line != NULL; line++) { if (strstr(*line, "DROP CONVERSION") != *line) PG_CMD_PUTLINE; free(*line); } free(conv_lines); PG_CMD_CLOSE; check_ok(); } /* * run privileges script * */ static void setup_privileges(void) { char *privileges_setup[] = { "UPDATE pg_class " " SET relacl = '{\"=r/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' " " WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;\n", "UPDATE pg_proc " " SET proacl = '{\"=X/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' " " WHERE proacl IS NULL;\n", "UPDATE pg_language " " SET lanacl = '{\"=U/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' " " WHERE lanpltrusted;\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n", "GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n", NULL }; PG_CMD_DECL; char **priv_lines; fputs("setting privileges on builtin objects ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; priv_lines = replace_token(privileges_setup, "$POSTGRES_SUPERUSERNAME", username); for (line = priv_lines; *line != NULL; line++) PG_CMD_PUTLINE; PG_CMD_CLOSE; check_ok(); } /* * extract the strange version of version required for info schema * */ static void set_info_version(void) { char *letterversion; long major = 0, minor = 0, micro = 0; char *endptr; char *vstr = xstrdup(PG_VERSION); char *ptr; ptr = vstr + (strlen(vstr) - 1); while (ptr != vstr && (*ptr < '0' || *ptr > '9')) ptr--; letterversion = ptr + 1; major = strtol(vstr, &endptr, 10); if (*endptr) minor = strtol(endptr + 1, &endptr, 10); if (*endptr) micro = strtol(endptr + 1, &endptr, 10); snprintf(infoversion, sizeof(infoversion), "%02ld.%02ld.%04ld%s", major, minor, micro, letterversion); } /* * load info schema and populate from features file * */ static void setup_schema(void) { PG_CMD_DECL; char **lines; int fres; fputs("creating information schema ... ", stdout); lines = readfile(info_schema_file); /* * note that here we don't run in single line mode, unlike other * places */ snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true -N template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; for (line = lines; *line != NULL; line++) { PG_CMD_PUTLINE; free(*line); } free(lines); 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); PG_CMD_OPEN; fres = fprintf(pg, "UPDATE information_schema.sql_implementation_info " " 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); 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(); } /* * clean everything up in template1 * */ static void vacuum_db(void) { PG_CMD_DECL_NOLINE; fputs("vacuuming database template1 ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; if (fputs("ANALYSE;\nVACUUM FULL FREEZE;\n", pg) < 0) exit_nicely(); fflush(pg); PG_CMD_CLOSE; check_ok(); } /* * copy template1 to template0 * */ static void make_template0(void) { char *template0_setup[] = { "CREATE DATABASE template0;\n", "UPDATE pg_database SET " " datistemplate = 't', " " datallowconn = 'f' " " WHERE datname = 'template0';\n", /* * We use the OID of template0 to determine lastsysoid * */ "UPDATE pg_database SET datlastsysoid = " " (SELECT oid::int4 - 1 FROM pg_database " " WHERE datname = 'template0');\n", /* * Explicitly revoke public create-schema and create-temp-table * privileges in template1 and template0; else the latter would be * on by default */ "REVOKE CREATE,TEMPORARY ON DATABASE template1 FROM public;\n", "REVOKE CREATE,TEMPORARY ON DATABASE template0 FROM public;\n", /* * Finally vacuum to clean up dead rows in pg_database */ "VACUUM FULL pg_database;\n", NULL }; PG_CMD_DECL; fputs("copying template1 to template0 ... ", stdout); snprintf(cmd, MAXPGPATH, "\"%s/postgres\" -F -O -c search_path=pg_catalog " "-c exit_on_error=true template1 >%s", pgpath, DEVNULL); PG_CMD_OPEN; for (line = template0_setup; *line; line++) PG_CMD_PUTLINE; PG_CMD_CLOSE; check_ok(); } /* * signal handler in case we are interrupted. * * The Windows runtime docs at * http://msdn.microsoft.com/library/en-us/vclib/html/_crt_signal.asp * specifically forbid a number of things being done from a signal handler, * including IO, memory allocation and system calls, and only allow jmpbuf * if you are handling SIGFPE. * * I avoided doing the forbidden things by setting a flag instead of calling * exit_nicely() directly. * * Also note the behaviour of Windows with SIGINT, which says this: * Note SIGINT is not supported for any Win32 application, including * Windows 98/Me and Windows NT/2000/XP. When a CTRL+C interrupt occurs, * Win32 operating systems generate a new thread to specifically handle * that interrupt. This can cause a single-thread application such as UNIX, * to become multithreaded, resulting in unexpected behavior. * * 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) { /* handle systems that reset the handler, like Windows (grr) */ pqsignal(signum, trapsig); not_ok = true; } /* * call exit_nicely() if we got a signal, or else output "ok". * */ static void check_ok() { if (not_ok) { puts("Caught Signal."); exit_nicely(); } else { /* no signal caught */ puts("ok"); } } /* * check if given string is a valid locle specifier * based on some code given to me by Peter Eisentraut * (but I take responsibility for it :-) */ static bool chklocale(const char *locale) { bool ret; int category = LC_CTYPE; char *save; save = setlocale(category, NULL); if (!save) return false; /* should not happen; */ save = xstrdup(save); ret = (setlocale(category, locale) != NULL); setlocale(category, save); free(save); /* should we exit here? */ if (!ret) fprintf(stderr, "%s: invalid locale name \"%s\"\n", progname, locale); return ret; } /* * set up the locale variables * assumes we have called setlocale(LC_ALL,"") * */ static void setlocales(void) { /* set empty lc_* values to locale config if set */ if (strlen(locale) > 0) { if (strlen(lc_ctype) == 0) lc_ctype = locale; if (strlen(lc_collate) == 0) lc_collate = locale; if (strlen(lc_numeric) == 0) lc_numeric = locale; if (strlen(lc_time) == 0) lc_time = locale; if (strlen(lc_monetary) == 0) lc_monetary = locale; if (strlen(lc_messages) == 0) lc_messages = locale; } /* * override absent/invalid config settings from initdb's locale * settings */ if (strlen(lc_ctype) == 0 || !chklocale(lc_ctype)) lc_ctype = xstrdup(setlocale(LC_CTYPE, NULL)); if (strlen(lc_collate) == 0 || !chklocale(lc_collate)) lc_collate = xstrdup(setlocale(LC_COLLATE, NULL)); if (strlen(lc_numeric) == 0 || !chklocale(lc_numeric)) lc_numeric = xstrdup(setlocale(LC_NUMERIC, NULL)); if (strlen(lc_time) == 0 || !chklocale(lc_time)) lc_time = xstrdup(setlocale(LC_TIME, NULL)); if (strlen(lc_monetary) == 0 || !chklocale(lc_monetary)) lc_monetary = xstrdup(setlocale(LC_MONETARY, NULL)); if (strlen(lc_messages) == 0 || !chklocale(lc_messages)) #ifdef LC_MESSAGES { /* when available get the current locale setting */ lc_messages = xstrdup(setlocale(LC_MESSAGES, NULL)); } #else { /* when not available, get the CTYPE setting */ lc_messages = xstrdup(setlocale(LC_CTYPE, NULL)); } #endif } /* * help text data * */ char *usage_text[] = { "$CMDNAME initializes a PostgreSQL database cluster.\n", "\n", "Usage:\n", " $CMDNAME [OPTION]... [DATADIR]\n", "\n", "Options:\n", " [-D, --pgdata=]DATADIR location for this database cluster\n", " -E, --encoding=ENCODING set default encoding for new databases\n", " --locale=LOCALE initialize database cluster with given locale\n", " --lc-collate, --lc-ctype, --lc-messages=LOCALE\n", " --lc-monetary, --lc-numeric, --lc-time=LOCALE\n", " initialize database cluster with given locale\n", " in the respective category (default taken from\n", " environment)\n", " --no-locale equivalent to --locale=C\n", " -U, --username=NAME database superuser name\n", " -W, --pwprompt prompt for a password for the new superuser\n", " -?, --help show this help, then exit\n", " -V, --version output version information, then exit\n", "\n", "Less commonly used options: \n", " -d, --debug generate lots of debugging output\n", " -s, --show show internal settings\n", " -L DIRECTORY where to find the input files\n", " -n, --noclean do not clean up after errors\n", "\n", "If the data directory is not specified, the environment variable PGDATA\n", "is used.\n", "\n", "Report bugs to <pgsql-bugs@postgresql.org>.\n", NULL }; /* * print help text * */ static void usage(void) { int i; char **newtext; newtext = replace_token(usage_text, "$CMDNAME", progname); for (i = 0; newtext[i]; i++) 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'}, {"encoding", required_argument, NULL, 'E'}, {"locale", required_argument, NULL, 1}, {"lc-collate", required_argument, NULL, 2}, {"lc-ctype", required_argument, NULL, 3}, {"lc-monetary", required_argument, NULL, 4}, {"lc-numeric", required_argument, NULL, 5}, {"lc-time", required_argument, NULL, 6}, {"lc-messages", required_argument, NULL, 7}, {"no-locale", no_argument, NULL, 8}, {"pwprompt", no_argument, NULL, 'W'}, {"username", required_argument, NULL, 'U'}, {"help", no_argument, NULL, '?'}, {"version", no_argument, NULL, 'V'}, {"debug", no_argument, NULL, 'd'}, {"show", no_argument, NULL, 's'}, {"noclean", no_argument, NULL, 'n'}, {0, 0, 0, 0} }; int c, i; int option_index; char *short_version; char *pgdenv; /* PGDATA value got from sent to * 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, ""); carg0 = xstrdup(argv[0]); canonicalize_path(carg0); lastsep = strrchr(carg0, '/'); progname = lastsep ? xstrdup(lastsep + 1) : carg0; #if defined(__CYGWIN__) || defined(WIN32) if (strlen(progname) > 4 && (exe = progname + (strlen(progname) - 4)) && stricmp(exe, EXE) == 0) { /* strip .exe suffix, regardless of case */ *exe = '\0'; } #endif if (lastsep) { self_path = carg0; *lastsep = '\0'; } else { /* no path known to ourselves from argv[0] */ self_path = NULL; } /* process options */ while (1) { /* * a : as the first option char here lets us use ? as a short * option */ c = getopt_long(argc, argv, ":D:E:WU:?sVdnL:", long_options, &option_index); if (c == -1) break; switch (c) { case 'D': pg_data = xstrdup(optarg); break; case 'E': encoding = xstrdup(optarg); break; case 'W': pwprompt = true; break; case 'U': username = xstrdup(optarg); break; case 'd': debug = true; break; case 'n': noclean = true; break; case 'L': datadir = xstrdup(optarg); break; case 1: locale = xstrdup(optarg); break; case 2: lc_collate = xstrdup(optarg); break; case 3: lc_ctype = xstrdup(optarg); break; case 4: lc_monetary = xstrdup(optarg); break; case 5: lc_numeric = xstrdup(optarg); break; case 6: lc_time = xstrdup(optarg); break; case 7: lc_messages = xstrdup(optarg); break; case 8: locale = "C"; break; case '?': show_help = true; break; case 's': show_setting = true; break; case 'V': show_version = true; break; default: show_help = true; printf("Unrecognized option: %c\n", c); } } if (optind < argc) { pg_data = xstrdup(argv[optind]); optind++; } set_info_version(); if (strlen(pg_data) == 0 && !(show_help || show_setting)) { pgdenv = getenv("PGDATA"); if (pgdenv && strlen(pgdenv)) { /* PGDATA found */ pg_data = xstrdup(pgdenv); } else { 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", progname); } } canonicalize_path(pg_data); /* * we have to set PGDATA for postgres rather than pass it on the * 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, "The program \"postgres\" is needed by %s " "but was not found in \n" "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); exit(1); } effective_user = get_id(); if (!strlen(username)) username = effective_user; if (strlen(encoding)) encodingid = get_encoding_id(encoding); set_input(&bki_file, "postgres.bki"); set_input(&desc_file, "postgres.description"); set_input(&hba_file, "pg_hba.conf.sample"); set_input(&ident_file, "pg_ident.conf.sample"); set_input(&conf_file, "postgresql.conf.sample"); set_input(&conversion_file, "conversion_create.sql"); set_input(&info_schema_file, "information_schema.sql"); set_input(&features_file, "sql_features.txt"); set_input(&system_views_file, "system_views.sql"); if (show_setting || debug) { fprintf(stderr, "VERSION=%s\n" "PGDATA=%s\ndatadir=%s\nPGPATH=%s\n" "ENCODING=%s\nENCODINGID=%s\n" "POSTGRES_SUPERUSERNAME=%s\nPOSTGRES_BKI=%s\n" "POSTGRES_DESCR=%s\nPOSTGRESQL_CONF_SAMPLE=%s\n" "PG_HBA_SAMPLE=%s\nPG_IDENT_SAMPLE=%s\n", PG_VERSION, pg_data, datadir, pgpath, encoding, encodingid, username, bki_file, desc_file, conf_file, hba_file, ident_file); } if (show_setting) exit(0); check_input(bki_file); check_input(desc_file); check_input(hba_file); check_input(ident_file); check_input(conf_file); check_input(conversion_file); check_input(info_schema_file); check_input(features_file); check_input(system_views_file); setlocales(); if (strcmp(lc_ctype, lc_collate) == 0 && strcmp(lc_ctype, lc_time) == 0 && strcmp(lc_ctype, lc_numeric) == 0 && strcmp(lc_ctype, lc_monetary) == 0 && strcmp(lc_ctype, lc_messages) == 0) { printf("The database cluster will be initialized with locale %s\n", lc_ctype); } else { printf("The database cluster will be initialized with locales\n" " COLLATE: %s\n" " CTYPE: %s\n" " MESSAGES: %s\n" " MONETARY: %s\n" " NUMERIC: %s\n" " TIME: %s\n", lc_collate, lc_ctype, lc_messages, lc_monetary, lc_numeric, lc_time); } umask(077); /* * now we are starting to do real work, trap signals so we can clean * up */ /* some of these are not valid on Windows */ #ifdef SIGHUP pqsignal(SIGHUP, trapsig); #endif #ifdef SIGINT pqsignal(SIGINT, trapsig); #endif #ifdef SIGQUIT pqsignal(SIGQUIT, trapsig); #endif #ifdef SIGTERM pqsignal(SIGTERM, trapsig); #endif /* clear this we'll use it in a few lines */ errno = 0; if (!check_data_dir()) { 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); exit(1); } /* * check_data_dir() called opendir - the errno should still be hanging * around */ if (errno == ENOENT) { printf("creating directory \"%s\" ... ", pg_data); if (!mkdatadir(NULL)) exit_nicely(); else check_ok(); made_new_pgdata = true; } for (i = 0; i < (sizeof(subdirs) / sizeof(char *)); i++) { printf("creating directory %s/%s ... ", pg_data, subdirs[i]); if (!mkdatadir(subdirs[i])) exit_nicely(); else check_ok(); } set_short_version(short_version, NULL); set_null_conf(); /* test connections first because it has more constraints */ test_connections(); test_buffers(); setup_config(); bootstrap_template1(short_version); set_short_version(short_version, "base/1"); setup_shadow(); if (pwprompt) get_set_pwd(); unlimit_systables(); setup_depend(); setup_sysviews(); setup_description(); setup_conversion(); setup_privileges(); setup_schema(); vacuum_db(); make_template0(); printf("\nSuccess. You can now start the database server using:\n\n" " %s%s%s/postmaster -D %s%s%s\n" "or\n" " %s%s%s/pg_ctl -D %s%s%s -l logfile start\n\n", QUOTE_PATH, pgpath, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH, QUOTE_PATH, pgpath, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH); return 0; }
Tom Lane wrote: > Bruce Momjian <pgman@candle.pha.pa.us> writes: > > Recreating the directory only happens on WIN32, where rmdir doesn't > > allow you to only delete files and subdirectories and not the parent > > directory. Non-Win32 does rm -rf dir/*. > > I think we should forget about invoking rm as a subprocess at all, and > just do the recursive directory walk and unlinks for ourselves. We > already have code to do this for copy in copydir.c, and unlink would not > be any longer. We will probably be forced into implementing database > removal for ourselves rather than by 'rm' hacks anyway as soon as > tablespaces come to pass; so why contort initdb's behavior for a very > transient implementation savings? If we want to do that, fine, but I don't want to force the change just for Win32. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
Tom Lane wrote: > Bruce Momjian <pgman@candle.pha.pa.us> writes: > >> Consider also the strong likelihood that the data directory's parent > >> directory is owned by root, > > > Again, this directory recreate happens only on Win32, an I thought it > > would be OK there. > > Windows has no concept of directory permissions at all? I thought the > more recent versions had at least rudimentary permissions. I found "del" works for what we need. I have posted the new code already. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
Tom Lane wrote: >Bruce Momjian <pgman@candle.pha.pa.us> writes: > > >>>Consider also the strong likelihood that the data directory's parent >>>directory is owned by root, >>> >>> > > > >>Again, this directory recreate happens only on Win32, an I thought it >>would be OK there. >> >> > >Windows has no concept of directory permissions at all? I thought the >more recent versions had at least rudimentary permissions. > More than rudimentary on the server versions. I found this info at http://www.cs.wayne.edu/labPages/modes.htm : Windows ACLs Windows NT and Windows 2000 uses Access Control Lists or ACLs to determine what operations a user may perform on a file. Windows ACLs allow one to set permissions with finer control that does the Unix file mode. For example, one can all[ow] a user to append data to a file as opposed to overwiting the file. ACLs also allow one to permit specific users to change the permissions on a file. Perhaps the biggest difference is that ACLs allow us to accord permissions on a user-by-user basis, rather than the three categories of users permitted by Unix file systems. This info applies to directories as well as plain files AFAIK cheers andrew
Also, I see this at the top of the code: * author: Andrew Dunstan mailto:andrew@dunslane.net * * Copyright (C) 2003 Andrew Dunstan * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California Can I remove the first copyright? -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
Bruce Momjian wrote: >Also, I see this at the top of the code: > > * author: Andrew Dunstan mailto:andrew@dunslane.net > * > * Copyright (C) 2003 Andrew Dunstan > * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group > * Portions Copyright (c) 1994, Regents of the University of California > >Can I remove the first copyright? > > Sure. I wasn't sure what our conventions were on that. cheers andrew
Bruce Momjian wrote: > > >I ran some tests using XP "CMD" and found: > > "C:\test" >and > "\test" > >works but: > > "test" > >does not work. Since I see that the output always has a leading path, > On Windows, pgpath is guaranteed to be a full path (see call to expanded_path() ) exactly so it works inside quotes using the Windows command processor. > I >have modified the code to do: > > printf("\nSuccess. You can now start the database server using:\n\n" > " %s%s%s/postmaster -D %s%s%s\n" > "or\n" > " %s%s%s/pg_ctl -D %s%s%s -l logfile start\n\n", > QUOTE_PATH, pgpath, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH, > QUOTE_PATH, pgpath, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH); > >I am attaching the updated initdb.c file. > > > The problem with this is that you now have 2 quoted strings. This is exactly the problem that I solved inside initdb by passing pg_data via the environment rather than on the command line. "help cmd" on XP gives you this info: If /C or /K is specified, then the remainder of the command line after the switch is processed as a command line, where the following logic is used to process quote (") characters: 1. If all of the following conditions are met, then quote characters on the command line are preserved: - no /S switch - exactly two quote characters - no special characters between the two quote characters, where special is one of: &<>()@^| - there are one or more whitespace characters between the the two quote characters - the string between the two quote characters is the name of an executable file. 2. Otherwise, old behavior is to see if the first character is a quote character and if so, strip the leading character and remove the last quote character on the command line, preserving any text after the last quote character. It is amazingly brain dead and cost me hours and hours of grief trying to work out WTF was going on. Offhand I can't think of a simple guaranteed to work command line for the Windows message. In the last resort we might need to look at having initdb create a .bat file or two for us. :-((( cheers andrew
Andrew Dunstan wrote: > > > > The problem with this is that you now have 2 quoted strings. I take this back. You *can* have multiple quoted strings on the command line - it just doesn't work in popen() because it uses the /C flag. Sorry for my confusion. cheers andrew
Andrew Dunstan wrote: > > > Andrew Dunstan wrote: > > > > > > > > > The problem with this is that you now have 2 quoted strings. > > > I take this back. You *can* have multiple quoted strings on the command > line - it just doesn't work in popen() because it uses the /C flag. Oh, OK, that's good. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
Patch applied. We now have a C version of initdb! --------------------------------------------------------------------------- Andrew Dunstan wrote: > > > Bruce Momjian wrote: > > >Here is a slightly modified version of Andrew's great work in making a C > >version of initdb. Other than minor cleanups, the only big change was > >to remove rmdir handling because we using rm -r and rmdir /s in > >commands/dbcommands.c, so we might as use the same thing for initdb.c > >rather than having code that traverses the directory tree doing 'rm'. > > > >The other change was to remove the unlink code and instead use > >port/dirmod.c's version. > > > >It passes all the regression tests. I have also included a diff against > >Andrew's version so you can see my changes. It seems Andrew had a very > >current version of initdb. The only update he missed was the change to > >test the number of connections before shared buffers --- I made that > >change myself. > > > >I would like to apply this in the next few days to HEAD before initdb.sh > >drifts. We are no longer using the WIN32_DEV branch because all our > >work is now in 7.5 head. > > > > > > My comments: > > I have no problem with shelling out to rmdir - although my goal was to > avoid shelling out to anything other than postgres itself. I think > recreating the datadir if we didn't create it initially should be OK in > that case, and it makes the code simpler. Not handling dot files in the > Unix case should also be fine, as we know the directory was empty to > start with and we don't create any. > > Regarding the #endif comments you removed - Peter had asked me to put > them in. See > http://archives.postgresql.org/pgsql-hackers/2003-10/msg00352.php > > You put a comment on canonicalise_path() asking if it is needed. The > short answer is very much "yes". The Windows command processor will > accept suitably quoted paths with forward slashes, but barfs badly with > mixed forward and back slashes. (See recent discussion on > hackers-win32).Removing the trailing slash on a path means we never get > ugly double slashes. Feel free to put that info in as a comment if you > think it is needed. > > The changes you made for the final message are not going to work for > Windows - if pgpath or pg_data contain spaces we need quotes (even on > Unix, although there most people know better than to put spaces in > names). Also see above about mixed slashes. Also, putting a \ before > pg_data will be nasty if it starts with a drive or network specifier - > or even without (you might turn a directory specifier into a network > drive specifier). It's just a message, though, and we shouldn't hold up > for that - we can patch it to get it right. > > (Getting the path and slash thing working portably was by far the > biggest headache in this project - the rest was just grunt work for the > most part, although I learned a few interesting things.) > > Otherwise I'm fine with this. > > cheers > > andrew > > > > > ---------------------------(end of broadcast)--------------------------- > TIP 6: Have you searched our list archives? > > http://archives.postgresql.org > -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
What's become of initdb's initial note about who you are? $ initdb The files belonging to this database system will be owned by user "postgres". This user must also own the server process. ... etc etc... The first two lines of printout don't seem to be in the "highly compatible" C version. regards, tom lane
Apologies. Here's the patch. There may be other very minor differences in wording, too. But if that's all I missed I won't think I did too badly :-) cheers andrew Tom Lane wrote: >What's become of initdb's initial note about who you are? > >$ initdb >The files belonging to this database system will be owned by user "postgres". >This user must also own the server process. >... etc etc... > >The first two lines of printout don't seem to be in the "highly >compatible" C version. > > > Index: initdb.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/bin/initdb/initdb.c,v retrieving revision 1.4 diff -c -w -r1.4 initdb.c *** initdb.c 13 Nov 2003 01:36:00 -0000 1.4 --- initdb.c 13 Nov 2003 04:15:33 -0000 *************** *** 2324,2329 **** --- 2324,2333 ---- pqsignal(SIGTERM, trapsig); #endif + printf("The files belonging to this database system will be owned by user %s.\n" + "This user must also own the server process.\n\n,", + efective_user); + /* clear this we'll use it in a few lines */ errno = 0;
ARRGGHH! It has a typo!. new patch shortly. Andrew Dunstan wrote: > Apologies. Here's the patch. There may be other very minor differences > in wording, too. But if that's all I missed I won't think I did too > badly :-) > > cheers > > andrew > > Tom Lane wrote: > >> What's become of initdb's initial note about who you are? >> >> $ initdb >> The files belonging to this database system will be owned by user >> "postgres". >> This user must also own the server process. >> ... etc etc... >> >> The first two lines of printout don't seem to be in the "highly >> compatible" C version. >> >> >> > > >
OK, this works. cheers andrew Andrew Dunstan wrote: > > ARRGGHH! It has a typo!. new patch shortly. > > Andrew Dunstan wrote: > >> Apologies. Here's the patch. There may be other very minor >> differences in wording, too. But if that's all I missed I won't think >> I did too badly :-) >> >> cheers >> >> andrew >> >> Tom Lane wrote: >> >>> What's become of initdb's initial note about who you are? >>> >>> $ initdb >>> The files belonging to this database system will be owned by user >>> "postgres". >>> This user must also own the server process. >>> ... etc etc... >>> >>> The first two lines of printout don't seem to be in the "highly >>> compatible" C version. >>> Index: initdb.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/bin/initdb/initdb.c,v retrieving revision 1.4 diff -c -w -r1.4 initdb.c *** initdb.c 13 Nov 2003 01:36:00 -0000 1.4 --- initdb.c 13 Nov 2003 04:32:18 -0000 *************** *** 2324,2329 **** --- 2324,2334 ---- pqsignal(SIGTERM, trapsig); #endif + printf("The files belonging to this database system will be owned " + "by user \"%s\".\n" + "This user must also own the server process.\n\n,", + effective_user); + /* clear this we'll use it in a few lines */ errno = 0;
Note to self: stop working when brain is clearly malfunctioning. *This* patch works. Please disregard previous. thanks andrew Andrew Dunstan wrote: > > OK, this works. > > cheers > > andrew > > Andrew Dunstan wrote: > >> >> ARRGGHH! It has a typo!. new patch shortly. >> >> Andrew Dunstan wrote: >> > ? initdb Index: initdb.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/bin/initdb/initdb.c,v retrieving revision 1.4 diff -c -w -r1.4 initdb.c *** initdb.c 13 Nov 2003 01:36:00 -0000 1.4 --- initdb.c 13 Nov 2003 12:52:21 -0000 *************** *** 2275,2280 **** --- 2275,2285 ---- check_input(features_file); check_input(system_views_file); + printf("The files belonging to this database system will be owned " + "by user \"%s\".\n" + "This user must also own the server process.\n\n", + effective_user); + setlocales(); if (strcmp(lc_ctype, lc_collate) == 0 &&
Patch applied. Thanks. --------------------------------------------------------------------------- Andrew Dunstan wrote: > Note to self: stop working when brain is clearly malfunctioning. > > *This* patch works. Please disregard previous. > > thanks > > andrew > > Andrew Dunstan wrote: > > > > > OK, this works. > > > > cheers > > > > andrew > > > > Andrew Dunstan wrote: > > > >> > >> ARRGGHH! It has a typo!. new patch shortly. > >> > >> Andrew Dunstan wrote: > >> > > > ? initdb > Index: initdb.c > =================================================================== > RCS file: /projects/cvsroot/pgsql-server/src/bin/initdb/initdb.c,v > retrieving revision 1.4 > diff -c -w -r1.4 initdb.c > *** initdb.c 13 Nov 2003 01:36:00 -0000 1.4 > --- initdb.c 13 Nov 2003 12:52:21 -0000 > *************** > *** 2275,2280 **** > --- 2275,2285 ---- > check_input(features_file); > check_input(system_views_file); > > + printf("The files belonging to this database system will be owned " > + "by user \"%s\".\n" > + "This user must also own the server process.\n\n", > + effective_user); > + > setlocales(); > > if (strcmp(lc_ctype, lc_collate) == 0 && > > ---------------------------(end of broadcast)--------------------------- > TIP 6: Have you searched our list archives? > > http://archives.postgresql.org -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073