From 130f89e93f59cdb4b160cbc31e6929de25d13794 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Sun, 11 Jul 2004 00:18:45 +0000 Subject: [PATCH] Allow configuration files to be placed outside the data directory. Add new postgresql.conf variables to point to data, pg_hba.conf, and pg_ident.conf files. Needs more documentation. --- doc/src/sgml/ref/postmaster.sgml | 27 ++++-- doc/src/sgml/runtime.sgml | 50 +++++++++- src/backend/bootstrap/bootstrap.c | 13 ++- src/backend/libpq/hba.c | 36 ++++--- src/backend/postmaster/postmaster.c | 91 ++++++++++++++--- src/backend/tcop/postgres.c | 14 +-- src/backend/utils/misc/guc-file.l | 97 +++++++++++++------ src/backend/utils/misc/guc.c | 37 ++++++- src/backend/utils/misc/postgresql.conf.sample | 10 ++ src/include/utils/guc.h | 9 +- 10 files changed, 303 insertions(+), 81 deletions(-) diff --git a/doc/src/sgml/ref/postmaster.sgml b/doc/src/sgml/ref/postmaster.sgml index 96bacf054a..0e22d34213 100644 --- a/doc/src/sgml/ref/postmaster.sgml +++ b/doc/src/sgml/ref/postmaster.sgml @@ -1,5 +1,5 @@ @@ -67,15 +67,28 @@ PostgreSQL documentation One postmaster always manages the data from exactly one database cluster. A database cluster is a collection of databases that is stored at a common file system - location. When the postmaster starts it needs to know the location - of the database cluster files (data area). This is - done with the invocation option or the - PGDATA environment variable; there is no default. - More than one postmaster process can run on a system at one time, - as long as they use different data areas and different + location. When the postmaster starts it needs + to know the location of the database cluster files (data + area). + More than one postmaster process can run on a system + at one time as long as they use different data areas and different communication ports (see below). A data area is created with . + + + The data area is specified by the option + or the PGDATA environment variable; there is no default. + Typically, it points to a directory created by + initdb. However, for administrative flexibility, you can + point to a directory containing only configuration files: + postgresql.conf, pg_hba.conf, and + pg_ident.conf. You can then set + postgresql.conf's pgdata to point to the + data directory. You can also point just to the server configuration file + like postgresql.conf and set its variables to point to the + other configuration files and the data directory. + diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 69871d474c..b249d9a5c2 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1,5 +1,5 @@ @@ -563,6 +563,54 @@ SET ENABLE_SEQSCAN TO OFF; any desired selection condition. + + Configuration Files + + + + + pgdata (string) + + + Specifies the directory to use for data storage (everything except + configuration files). + + + + + + hba_conf (string) + + + Specifies the file name to use for configuration of host-based + authentication (HBA). + + + + + + ident_conf (string) + + + Specifies the file name to use for configuration of + ident authentication. + + + + + + external_pidfile (string) + + + Specifies the location of an additional postmaster + process-id (PID) file for use by server administration programs. + + + + + + + Connections and Authentication diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index ccf0595835..7fe2ea02a6 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.185 2004/06/24 21:02:24 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.186 2004/07/11 00:18:43 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -212,7 +212,7 @@ BootstrapMain(int argc, char *argv[]) char *dbname; int flag; int xlogop = BS_XLOG_NOP; - char *potential_DataDir = NULL; + char *userPGDATA = NULL; /* * initialize globals @@ -236,8 +236,7 @@ BootstrapMain(int argc, char *argv[]) if (!IsUnderPostmaster) { InitializeGUCOptions(); - potential_DataDir = getenv("PGDATA"); /* Null if no PGDATA - * variable */ + userPGDATA = getenv("PGDATA"); /* Null if no PGDATA variable */ } /* Ignore the initial -boot argument, if present */ @@ -252,7 +251,7 @@ BootstrapMain(int argc, char *argv[]) switch (flag) { case 'D': - potential_DataDir = optarg; + userPGDATA = optarg; break; case 'd': { @@ -326,7 +325,7 @@ BootstrapMain(int argc, char *argv[]) if (!IsUnderPostmaster) { - if (!potential_DataDir) + if (!userPGDATA) { write_stderr("%s does not know where to find the database system data.\n" "You must specify the directory that contains the database system\n" @@ -335,7 +334,7 @@ BootstrapMain(int argc, char *argv[]) argv[0]); proc_exit(1); } - SetDataDir(potential_DataDir); + SetDataDir(userPGDATA); } /* Validate we have been given a reasonable-looking DataDir */ diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index d15e8f6aff..4577aec493 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.125 2004/05/30 23:40:26 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.126 2004/07/11 00:18:43 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -35,6 +35,7 @@ #include "miscadmin.h" #include "nodes/pg_list.h" #include "storage/fd.h" +#include "utils/guc.h" /* Max size of username ident server can return */ @@ -1029,17 +1030,22 @@ load_user(void) void load_hba(void) { - int bufsize; FILE *file; /* The config file we have to read */ char *conf_file; /* The name of the config file */ if (hba_lines || hba_line_nums) free_lines(&hba_lines, &hba_line_nums); - /* Put together the full pathname to the config file. */ - bufsize = (strlen(DataDir) + strlen(CONF_FILE) + 2) * sizeof(char); - conf_file = (char *) palloc(bufsize); - snprintf(conf_file, bufsize, "%s/%s", DataDir, CONF_FILE); + /* HBA filename in config file */ + if (guc_hbafile) + conf_file = pstrdup(guc_hbafile); + else + { + char *confloc = (user_pgconfig_is_dir) ? user_pgconfig : DataDir; + /* put together the full pathname to the config file */ + conf_file = palloc(strlen(confloc) + strlen(CONF_FILE) + 2); + sprintf(conf_file, "%s/%s", confloc, CONF_FILE); + } file = AllocateFile(conf_file, "r"); if (file == NULL) @@ -1178,16 +1184,20 @@ load_ident(void) FILE *file; /* The map file we have to read */ char *map_file; /* The name of the map file we have to * read */ - int bufsize; - if (ident_lines || ident_line_nums) free_lines(&ident_lines, &ident_line_nums); - /* put together the full pathname to the map file */ - bufsize = (strlen(DataDir) + strlen(USERMAP_FILE) + 2) * sizeof(char); - map_file = (char *) palloc(bufsize); - snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE); - + /* IDENT filename in config file */ + if (guc_identfile) + map_file = pstrdup(guc_identfile); + else + { + /* put together the full pathname to the map file */ + char *confloc = (user_pgconfig_is_dir) ? user_pgconfig : DataDir; + map_file = (char *) palloc(strlen(confloc) + strlen(USERMAP_FILE) + 2); + sprintf(map_file, "%s/%s", confloc, USERMAP_FILE); + } + file = AllocateFile(map_file, "r"); if (file == NULL) { diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index ee934711ff..d68279b7f6 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.406 2004/07/10 23:29:16 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.407 2004/07/11 00:18:43 momjian Exp $ * * NOTES * @@ -233,6 +233,7 @@ extern int optreset; * postmaster.c - function prototypes */ static void checkDataDir(const char *checkdir); +static bool onlyConfigSpecified(const char *checkdir); #ifdef USE_RENDEZVOUS static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context); @@ -306,7 +307,7 @@ PostmasterMain(int argc, char *argv[]) { int opt; int status; - char *potential_DataDir = NULL; + char *userPGDATA = NULL; int i; progname = get_progname(argv[0]); @@ -370,7 +371,7 @@ PostmasterMain(int argc, char *argv[]) */ InitializeGUCOptions(); - potential_DataDir = getenv("PGDATA"); /* default value */ + userPGDATA = getenv("PGDATA"); /* default value */ opterr = 1; @@ -395,7 +396,7 @@ PostmasterMain(int argc, char *argv[]) /* Can no longer set the backend executable file to use. */ break; case 'D': - potential_DataDir = optarg; + userPGDATA = optarg; break; case 'd': { @@ -524,13 +525,49 @@ PostmasterMain(int argc, char *argv[]) ExitPostmaster(1); } - /* - * Now we can set the data directory, and then read postgresql.conf. - */ - checkDataDir(potential_DataDir); /* issues error messages */ - SetDataDir(potential_DataDir); + if (onlyConfigSpecified(userPGDATA)) + { + /* + * It is either a file name or a directory with no + * global/pg_control file, and hence not a data directory. + */ + user_pgconfig = userPGDATA; + ProcessConfigFile(PGC_POSTMASTER); - ProcessConfigFile(PGC_POSTMASTER); + if (!guc_pgdata) /* Got a pgdata from the config file? */ + { + write_stderr("%s does not know where to find the database system data.\n" + "This should be specified as 'pgdata' in %s%s.\n", + progname, userPGDATA, + user_pgconfig_is_dir ? "/postgresql.conf" : ""); + ExitPostmaster(2); + } + checkDataDir(guc_pgdata); + SetDataDir(guc_pgdata); + } + else + { + /* Now we can set the data directory, and then read postgresql.conf. */ + checkDataDir(userPGDATA); + SetDataDir(userPGDATA); + ProcessConfigFile(PGC_POSTMASTER); + } + + if (external_pidfile) + { + FILE *fpidfile = fopen(external_pidfile, "w"); + + if (fpidfile) + { + fprintf(fpidfile, "%d\n", MyProcPid); + fclose(fpidfile); + /* Should we remove the pid file on postmaster exit? */ + } + else + fprintf(stderr, + gettext("%s could not write to external pid file %s\n"), + progname, external_pidfile); + } /* If timezone is not set, determine what the OS uses */ pg_timezone_initialize(); @@ -848,6 +885,32 @@ PostmasterMain(int argc, char *argv[]) } + +static bool +onlyConfigSpecified(const char *checkdir) +{ + char path[MAXPGPATH]; + struct stat stat_buf; + + if (checkdir == NULL) /* checkDataDir handles this */ + return FALSE; + + if (stat(checkdir, &stat_buf) == -1) /* ditto */ + return FALSE; + + if (S_ISREG(stat_buf.st_mode)) /* It's a regular file, so assume it's explict */ + return TRUE; + else if (S_ISDIR(stat_buf.st_mode)) /* It's a directory, is it a config or system dir? */ + { + snprintf(path, MAXPGPATH, "%s/global/pg_control", checkdir); + /* If this is not found, it is a config-only directory */ + if (stat(path, &stat_buf) == -1) + return TRUE; + } + return FALSE; +} + + /* * Validate the proposed data directory */ @@ -862,8 +925,8 @@ checkDataDir(const char *checkdir) { write_stderr("%s does not know where to find the database system data.\n" "You must specify the directory that contains the database system\n" - "either by specifying the -D invocation option or by setting the\n" - "PGDATA environment variable.\n", + "or configuration files by either specifying the -D invocation option\n" + "or by setting the PGDATA environment variable.\n", progname); ExitPostmaster(2); } @@ -873,12 +936,12 @@ checkDataDir(const char *checkdir) if (errno == ENOENT) ereport(FATAL, (errcode_for_file_access(), - errmsg("data directory \"%s\" does not exist", + errmsg("data or configuration location \"%s\" does not exist", checkdir))); else ereport(FATAL, (errcode_for_file_access(), - errmsg("could not read permissions of directory \"%s\": %m", + errmsg("could not read permissions of \"%s\": %m", checkdir))); } diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index c42bd6c7bf..54ba0a1fe6 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.422 2004/07/01 00:51:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.423 2004/07/11 00:18:44 momjian Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -2164,7 +2164,7 @@ PostgresMain(int argc, char *argv[], const char *username) { int flag; const char *dbname = NULL; - char *potential_DataDir = NULL; + char *userPGDATA = NULL; bool secure; int errs = 0; int debug_flag = 0; @@ -2235,7 +2235,7 @@ PostgresMain(int argc, char *argv[], const char *username) if (!IsUnderPostmaster) { InitializeGUCOptions(); - potential_DataDir = getenv("PGDATA"); + userPGDATA = getenv("PGDATA"); } /* ---------------- @@ -2283,7 +2283,7 @@ PostgresMain(int argc, char *argv[], const char *username) case 'D': /* PGDATA directory */ if (secure) - potential_DataDir = optarg; + userPGDATA = optarg; break; case 'd': /* debug level */ @@ -2574,12 +2574,12 @@ PostgresMain(int argc, char *argv[], const char *username) * set up handler to log session end. */ if (IsUnderPostmaster && Log_disconnections) - on_proc_exit(log_disconnections,0); + on_proc_exit(log_disconnections, 0); } if (!IsUnderPostmaster) { - if (!potential_DataDir) + if (!userPGDATA) { write_stderr("%s does not know where to find the database system data.\n" "You must specify the directory that contains the database system\n" @@ -2588,7 +2588,7 @@ PostgresMain(int argc, char *argv[], const char *username) argv[0]); proc_exit(1); } - SetDataDir(potential_DataDir); + SetDataDir(userPGDATA); } Assert(DataDir); diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l index cb95347e59..08b4ec4ad4 100644 --- a/src/backend/utils/misc/guc-file.l +++ b/src/backend/utils/misc/guc-file.l @@ -4,7 +4,7 @@ * * Copyright (c) 2000-2003, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.22 2004/05/26 15:07:38 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.23 2004/07/11 00:18:44 momjian Exp $ */ %{ @@ -129,43 +129,24 @@ free_name_value_list(struct name_value_pair * list) * function does not return if an error occurs. If an error occurs, no * values will be changed. */ -void -ProcessConfigFile(GucContext context) +static void +ReadConfigFile(char *filename, GucContext context) { int token, parse_state; char *opt_name, *opt_value; - char *filename; struct name_value_pair *item, *head, *tail; int elevel; FILE * fp; - Assert(context == PGC_POSTMASTER || context == PGC_BACKEND - || context == PGC_SIGHUP); - Assert(DataDir); elevel = (context == PGC_SIGHUP) ? DEBUG4 : ERROR; - /* - * Open file - */ - filename = malloc(strlen(DataDir) + strlen(CONFIG_FILENAME) + 2); - if (filename == NULL) - { - ereport(elevel, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); - return; - } - sprintf(filename, "%s/" CONFIG_FILENAME, DataDir); - fp = AllocateFile(filename, "r"); if (!fp) { free(filename); - /* File not found is fine */ - if (errno != ENOENT) - ereport(elevel, - (errcode_for_file_access(), - errmsg("could not open configuration file \"%s\": %m", CONFIG_FILENAME))); + ereport(elevel, + (errcode_for_file_access(), + errmsg("could not open configuration file \"%s\": %m", filename))); return; } @@ -197,7 +178,8 @@ ProcessConfigFile(GucContext context) token = yylex(); if (token != GUC_ID && token != GUC_STRING && - token != GUC_INTEGER && token != GUC_REAL && + token != GUC_INTEGER && + token != GUC_REAL && token != GUC_UNQUOTED_STRING) goto parse_error; opt_value = strdup(yytext); @@ -259,7 +241,6 @@ ProcessConfigFile(GucContext context) } FreeFile(fp); - free(filename); /* * Check if all options are valid @@ -282,12 +263,11 @@ ProcessConfigFile(GucContext context) parse_error: FreeFile(fp); - free(filename); free_name_value_list(head); ereport(elevel, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error in file \"%s\" line %u, near token \"%s\"", - CONFIG_FILENAME, ConfigFileLineno, yytext))); + filename, ConfigFileLineno, yytext))); return; out_of_memory: @@ -300,6 +280,65 @@ ProcessConfigFile(GucContext context) return; } +/* + * Function to read and process the configuration file. The + * parameter indicates the context that the file is being read + * (postmaster startup, backend startup, or SIGHUP). All options + * mentioned in the configuration file are set to new values. This + * function does not return if an error occurs. If an error occurs, no + * values will be changed. + */ +void +ProcessConfigFile(GucContext context) +{ + char *filename; + + Assert(context == PGC_POSTMASTER || context == PGC_BACKEND || context == PGC_SIGHUP); + + /* Added for explicit config file */ + if (user_pgconfig) + { + struct stat sb; + + if (stat(user_pgconfig, &sb) != 0) + { + int elevel = (context == PGC_SIGHUP) ? DEBUG3 : ERROR; + elog(elevel, "Configuration file \"%s\" does not exist", user_pgconfig); + return; + } + + if (S_ISDIR(sb.st_mode)) + { + /* This will cause a small one time memory leak + * if the user also specifies hba_conf, + * ident_conf, and data_dir + */ + filename = malloc(strlen(user_pgconfig) + strlen(CONFIG_FILENAME) + 2); + sprintf(filename, "%s/%s", user_pgconfig, CONFIG_FILENAME); + user_pgconfig_is_dir = true; + } + else + filename = strdup(user_pgconfig); /* Use explicit file */ + } + else + { + /* Use datadir for config */ + filename = malloc(strlen(DataDir) + strlen(CONFIG_FILENAME) + 2); + sprintf(filename, "%s/%s", DataDir, CONFIG_FILENAME); + } + + if (filename == NULL) + { + int elevel = (context == PGC_SIGHUP) ? DEBUG3 : ERROR; + elog(elevel, "out of memory"); + return; + } + + ReadConfigFile(filename, context); + + free(filename); +} + /* ---------------- diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 7e4f0ebc00..9c8f8b5cc6 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut . * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.213 2004/07/05 23:14:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.214 2004/07/11 00:18:44 momjian Exp $ * *-------------------------------------------------------------------- */ @@ -57,6 +57,13 @@ #include "utils/pg_locale.h" #include "pgstat.h" +char *guc_pgdata; +char *guc_hbafile; +char *guc_identfile; +char *external_pidfile; + +char *user_pgconfig = NULL; +bool user_pgconfig_is_dir = false; #ifndef PG_KRB_SRVTAB #define PG_KRB_SRVTAB "" @@ -107,6 +114,7 @@ static bool assign_stage_log_stats(bool newval, bool doit, GucSource source); static bool assign_log_stats(bool newval, bool doit, GucSource source); static bool assign_transaction_read_only(bool newval, bool doit, GucSource source); +static void ReadConfigFile(char *filename, GucContext context); /* * Debugging options @@ -1701,15 +1709,40 @@ static struct config_string ConfigureNamesString[] = NULL, assign_custom_variable_classes, NULL }, + { + {"pgdata", PGC_POSTMASTER, 0, gettext_noop("Sets the location of the data directory"), NULL}, + &guc_pgdata, + NULL, NULL, NULL + }, + + { + {"hba_conf", PGC_SIGHUP, 0, gettext_noop("Sets the location of the \"hba\" configuration file"), NULL}, + &guc_hbafile, + NULL, NULL, NULL + }, + + { + {"ident_conf", PGC_SIGHUP, 0, gettext_noop("Sets the location of the \"ident\" configuration file"), NULL}, + &guc_identfile, + NULL, NULL, NULL + }, + + { + {"external_pidfile", PGC_POSTMASTER, 0, gettext_noop("Writes the postmaster PID to the specified file"), NULL}, + &external_pidfile, + NULL, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL } }; -/******** end of options list ********/ +/******** end of options list ********/ + /* * To allow continued support of obsolete names for GUC variables, we apply * the following mappings to any unrecognized name. Note that an old name diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 7983abbf7f..bc772b2a7a 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -21,6 +21,16 @@ # "pg_ctl reload". +#--------------------------------------------------------------------------- +# CONFIGURATION FILES +#--------------------------------------------------------------------------- + +# pgdata = '/usr/local/pgsql/data' # use data in another directory +# hba_conf = '/etc/pgsql/pg_hba.conf' # use hba info in another directory +# ident_conf = '/etc/pgsql/pg_ident.conf' # use ident info in another directory +# external_pidfile= '/var/run/postgresql.pid' # write an extra pid file + + #--------------------------------------------------------------------------- # CONNECTIONS AND AUTHENTICATION #--------------------------------------------------------------------------- diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 0a510cd665..2a58ad048a 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -7,7 +7,7 @@ * Copyright (c) 2000-2003, PostgreSQL Global Development Group * Written by Peter Eisentraut . * - * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.48 2004/07/01 00:51:44 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.49 2004/07/11 00:18:45 momjian Exp $ *-------------------------------------------------------------------- */ #ifndef GUC_H @@ -135,6 +135,13 @@ extern int log_min_messages; extern int client_min_messages; extern int log_min_duration_statement; +extern char *guc_pgdata; +extern char *guc_hbafile; +extern char *guc_identfile; +extern char *external_pidfile; + +extern char *user_pgconfig; +extern bool user_pgconfig_is_dir; extern void SetConfigOption(const char *name, const char *value, GucContext context, GucSource source); -- 2.40.0