📄 initdb.c
字号:
/*------------------------------------------------------------------------- * * initdb --- initialize a PostgreSQL installation * * initdb creates (initializes) a PostgreSQL database cluster (site, * instance, installation, whatever). A database cluster is a * collection of PostgreSQL databases all managed by the same postmaster. * * To create the database cluster, we create the directory that contains * all its data, create the files that hold the global tables, create * a few other control files for it, and create three databases: the * template databases "template0" and "template1", and a default user * database "postgres". * * The template databases are ordinary PostgreSQL databases. template0 * is never supposed to change after initdb, whereas template1 can be * changed to add site-local standard data. Either one can be copied * to produce a new database. * * For largely-historical reasons, the template1 database is the one built * by the basic bootstrap process. After it is complete, template0 and * the default database, postgres, are made just by copying template1. * * To create template1, we run the postgres (backend) program in bootstrap * mode and feed it data from the postgres.bki library file. After this * initial bootstrap phase, some additional stuff is created by normal * SQL commands fed to a standalone backend. Some of those commands are * just embedded into this program (yeah, it's ugly), but larger chunks * are taken from script files. * * * Note: * The program has some memory leakage - it isn't worth cleaning it up. * * This is a C implementation of the previous shell script for setting up a * PostgreSQL cluster location, and should be highly compatible with it. * author of C translation: Andrew Dunstan mailto:andrew@dunslane.net * * This code is released under the terms of the PostgreSQL License. * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * Portions taken from FreeBSD. * * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.99.2.2 2006/02/24 00:55:27 adunstan Exp $ * *------------------------------------------------------------------------- */#include "postgres_fe.h"#include <dirent.h>#include <sys/stat.h>#include <unistd.h>#include <locale.h>#include <signal.h>#ifdef HAVE_LANGINFO_H#include <langinfo.h>#endif#include "libpq/pqsignal.h"#include "mb/pg_wchar.h"#include "getaddrinfo.h"#include "getopt_long.h"#ifndef HAVE_INT_OPTRESETint optreset;#endif/* version string we expect back from postgres */#define PG_VERSIONSTR "postgres (PostgreSQL) " PG_VERSION "\n"/* * these values are passed in by makefile defines */static char *share_path = NULL;/* values to be obtained from arguments */static char *pg_data = "";static char *encoding = "";static char *locale = "";static char *lc_collate = "";static char *lc_ctype = "";static char *lc_monetary = "";static char *lc_numeric = "";static char *lc_time = "";static char *lc_messages = "";static char *username = "";static bool pwprompt = false;static char *pwfilename = NULL;static char *authmethod = "";static bool debug = false;static bool noclean = false;static bool show_setting = false;/* internal vars */static const char *progname;static char *encodingid = "0";static char *bki_file;static char *desc_file;static char *hba_file;static char *ident_file;static char *conf_file;static char *conversion_file;static char *info_schema_file;static char *features_file;static char *system_views_file;static bool made_new_pgdata = false;static bool found_existing_pgdata = false;static char infoversion[100];static bool caught_signal = false;static bool output_failed = false;static int output_errno = 0;/* defaults */static int n_connections = 10;static int n_buffers = 50;/* * Warning messages for authentication methods */#define AUTHTRUST_WARNING \"# CAUTION: Configuring the system for local \"trust\" authentication allows\n" \"# any local user to connect as any PostgreSQL user, including the database\n" \"# superuser. If you do not trust all your local users, use another\n" \"# authentication method.\n"static char *authwarning = NULL;/* * Centralized knowledge of switches to pass to backend * * Note: in the shell-script version, we also passed PGDATA as a -D switch, * but here it is more convenient to pass it as an environment variable * (no quoting to worry about). */static const char *boot_options = "-F";static const char *backend_options = "-F -O -c search_path=pg_catalog -c exit_on_error=true";/* path to 'initdb' binary directory */static char bin_path[MAXPGPATH];static char backend_exec[MAXPGPATH];static void *pg_malloc(size_t size);static char *xstrdup(const char *s);static char **replace_token(char **lines, const char *token, const char *replacement);#ifndef HAVE_UNIX_SOCKETSstatic char **filter_lines_with_token(char **lines, const char *token);#endifstatic char **readfile(char *path);static void writefile(char *path, char **lines);static FILE *popen_check(const char *command, const char *mode);static int mkdir_p(char *path, mode_t omode);static void exit_nicely(void);static char *get_id(void);static char *get_encoding_id(char *encoding_name);static char *get_short_version(void);static int check_data_dir(void);static bool mkdatadir(const char *subdir);static void set_input(char **dest, char *filename);static void check_input(char *path);static void set_short_version(char *short_version, char *extrapath);static void set_null_conf(void);static void test_connections(void);static void test_buffers(void);static void setup_config(void);static void bootstrap_template1(char *short_version);static void setup_auth(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 make_postgres(void);static void trapsig(int signum);static void check_ok(void);static char *escape_quotes(const char *src);static bool chklocale(const char *locale);static void setlocales(void);static void usage(const char *progname);/* * macros for running pipes to postgres */#define PG_CMD_DECL char cmd[MAXPGPATH]; FILE *cmdfd#define PG_CMD_OPEN \do { \ cmdfd = popen_check(cmd, "w"); \ if (cmdfd == NULL) \ exit_nicely(); /* message already printed by popen_check */ \} while (0)#define PG_CMD_CLOSE \do { \ if (pclose_check(cmdfd)) \ exit_nicely(); /* message already printed by pclose_check */ \} while (0)#define PG_CMD_PUTS(line) \do { \ if (fputs(line, cmdfd) < 0 || fflush(cmdfd) < 0) \ output_failed = true, output_errno = errno; \} while (0)#define PG_CMD_PRINTF1(fmt, arg1) \do { \ if (fprintf(cmdfd, fmt, arg1) < 0 || fflush(cmdfd) < 0) \ output_failed = true, output_errno = errno; \} while (0)#define PG_CMD_PRINTF2(fmt, arg1, arg2) \do { \ if (fprintf(cmdfd, fmt, arg1, arg2) < 0 || fflush(cmdfd) < 0) \ output_failed = true, output_errno = errno; \} while (0)#ifndef WIN32#define QUOTE_PATH ""#define DIR_SEP "/"#else#define QUOTE_PATH "\""#define DIR_SEP "\\"#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 *pg_malloc(size_t size){ void *result; result = malloc(size); if (!result) { fprintf(stderr, _("%s: out of memory\n"), progname); exit(1); } return result;}static char *xstrdup(const char *s){ char *result; result = strdup(s); if (!result) { fprintf(stderr, _("%s: out of memory\n"), progname); exit(1); } return result;}/* * 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, const char *token, const char *replacement){ int numlines = 1; int i; char **result; int toklen, replen, diff; for (i = 0; lines[i]; i++) numlines++; result = (char **) pg_malloc(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 *) pg_malloc(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;}/* * make a copy of lines without any that contain the token * * a sort of poor man's grep -v */#ifndef HAVE_UNIX_SOCKETSstatic char **filter_lines_with_token(char **lines, const char *token){ int numlines = 1; int i, src, dst; char **result; for (i = 0; lines[i]; i++) numlines++; result = (char **) pg_malloc(numlines * sizeof(char *)); for (src = 0, dst = 0; src < numlines; src++) { if (lines[src] == NULL || strstr(lines[src], token) == NULL) result[dst++] = lines[src]; } return result;}#endif/* * 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, _("%s: could not open file \"%s\" for reading: %s\n"), progname, path, strerror(errno)); 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 **) pg_malloc((nlines + 2) * sizeof(char *)); buffer = (char *) pg_malloc(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 * * This is only used to write text files. Use fopen "w" not PG_BINARY_W * so that the resulting configuration files are nicely editable on Windows. */static voidwritefile(char *path, char **lines){ FILE *out_file; char **line; if ((out_file = fopen(path, "w")) == NULL) { fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"), progname, path, strerror(errno)); exit_nicely(); } for (line = lines; *line != NULL; line++) { if (fputs(*line, out_file) < 0) { fprintf(stderr, _("%s: could not write file \"%s\": %s\n"), progname, path, strerror(errno)); exit_nicely(); } free(*line); } if (fclose(out_file)) { fprintf(stderr, _("%s: could not write file \"%s\": %s\n"), progname, path, strerror(errno)); exit_nicely(); }}/* * Open a subcommand with suitable error messaging */static FILE *popen_check(const char *command, const char *mode){ FILE *cmdfd; fflush(stdout); fflush(stderr); errno = 0; cmdfd = popen(command, mode); if (cmdfd == NULL) fprintf(stderr, _("%s: could not execute command \"%s\": %s\n"), progname, command, strerror(errno)); return cmdfd;}/* 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. * * note that on failure, the path arg has been modified to show the particular * directory level we had problems with. */static intmkdir_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 (!last && 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 caused 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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -