Remove the use of the pg_auth flat file for client authentication.

(That flat file is now completely useless, but removal will come later.)

To do this, postpone client authentication into the startup transaction
that's run by InitPostgres.  We still collect the startup packet and do
SSL initialization (if needed) at the same time we did before.  The
AuthenticationTimeout is applied separately to startup packet collection
and the actual authentication cycle.  (This is a bit annoying, since it
means a couple extra syscalls; but the signal handling requirements inside
and outside a transaction are sufficiently different that it seems best
to treat the timeouts as completely independent.)

A small security disadvantage is that if the given database name is invalid,
this will be reported to the client before any authentication happens.
We could work around that by connecting to database "postgres" instead,
but consensus seems to be that it's not worth introducing such surprising
behavior.

Processing of all command-line switches and GUC options received from the
client is now postponed until after authentication.  This means that
PostAuthDelay is much less useful than it used to be --- if you need to
investigate problems during InitPostgres you'll have to set PreAuthDelay
instead.  However, allowing an unauthenticated user to set any GUC options
whatever seems a bit too risky, so we'll live with that.
This commit is contained in:
Tom Lane 2009-08-29 19:26:52 +00:00
parent 585806cb9f
commit e710b65c1c
15 changed files with 535 additions and 604 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.183 2009/06/25 11:30:08 mha Exp $
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.184 2009/08/29 19:26:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -33,8 +33,10 @@
#include "libpq/ip.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "storage/ipc.h"
/*----------------------------------------------------------------
* Global authentication functions
*----------------------------------------------------------------
@ -281,6 +283,15 @@ ClientAuthentication(Port *port)
errmsg("missing or erroneous pg_hba.conf file"),
errhint("See server log for details.")));
/*
* Enable immediate response to SIGTERM/SIGINT/timeout interrupts.
* (We don't want this during hba_getauthmethod() because it might
* have to do database access, eg for role membership checks.)
*/
ImmediateInterruptOK = true;
/* And don't forget to detect one that already arrived */
CHECK_FOR_INTERRUPTS();
/*
* This is the first point where we have access to the hba record for the
* current connection, so perform any verifications based on the hba
@ -458,6 +469,9 @@ ClientAuthentication(Port *port)
sendAuthRequest(port, AUTH_REQ_OK);
else
auth_failed(port, status);
/* Done with authentication, so we should turn off immediate interrupts */
ImmediateInterruptOK = false;
}
@ -690,9 +704,6 @@ pg_krb5_recvauth(Port *port)
char *kusername;
char *cp;
if (get_role_line(port->user_name) == NULL)
return STATUS_ERROR;
ret = pg_krb5_init(port);
if (ret != STATUS_OK)
return ret;
@ -1823,9 +1834,6 @@ authident(hbaPort *port)
{
char ident_user[IDENT_USERNAME_MAX + 1];
if (get_role_line(port->user_name) == NULL)
return STATUS_ERROR;
switch (port->raddr.addr.ss_family)
{
case AF_INET:

View File

@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/libpq/crypt.c,v 1.77 2009/01/01 17:23:42 momjian Exp $
* $PostgreSQL: pgsql/src/backend/libpq/crypt.c,v 1.78 2009/08/29 19:26:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,38 +20,63 @@
#include <crypt.h>
#endif
#include "catalog/pg_authid.h"
#include "libpq/crypt.h"
#include "libpq/md5.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
int
md5_crypt_verify(const Port *port, const char *role, char *client_pass)
{
char *shadow_pass = NULL,
*valuntil = NULL,
*crypt_pwd;
int retval = STATUS_ERROR;
List **line;
ListCell *token;
char *shadow_pass,
*crypt_pwd;
TimestampTz vuntil = 0;
char *crypt_client_pass = client_pass;
HeapTuple roleTup;
Datum datum;
bool isnull;
if ((line = get_role_line(role)) == NULL)
return STATUS_ERROR;
/*
* Disable immediate interrupts while doing database access. (Note
* we don't bother to turn this back on if we hit one of the failure
* conditions, since we can expect we'll just exit right away anyway.)
*/
ImmediateInterruptOK = false;
/* Skip over rolename */
token = list_head(*line);
if (token)
token = lnext(token);
if (token)
/* Get role info from pg_authid */
roleTup = SearchSysCache(AUTHNAME,
PointerGetDatum(role),
0, 0, 0);
if (!HeapTupleIsValid(roleTup))
return STATUS_ERROR; /* no such user */
datum = SysCacheGetAttr(AUTHNAME, roleTup,
Anum_pg_authid_rolpassword, &isnull);
if (isnull)
{
shadow_pass = (char *) lfirst(token);
token = lnext(token);
if (token)
valuntil = (char *) lfirst(token);
ReleaseSysCache(roleTup);
return STATUS_ERROR; /* user has no password */
}
shadow_pass = TextDatumGetCString(datum);
if (shadow_pass == NULL || *shadow_pass == '\0')
return STATUS_ERROR;
datum = SysCacheGetAttr(AUTHNAME, roleTup,
Anum_pg_authid_rolvaliduntil, &isnull);
if (!isnull)
vuntil = DatumGetTimestampTz(datum);
ReleaseSysCache(roleTup);
if (*shadow_pass == '\0')
return STATUS_ERROR; /* empty password */
/* Re-enable immediate response to SIGTERM/SIGINT/timeout interrupts */
ImmediateInterruptOK = true;
/* And don't forget to detect one that already arrived */
CHECK_FOR_INTERRUPTS();
/*
* Compare with the encrypted or plain password depending on the
@ -119,24 +144,14 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass)
if (strcmp(crypt_client_pass, crypt_pwd) == 0)
{
/*
* Password OK, now check to be sure we are not past valuntil
* Password OK, now check to be sure we are not past rolvaliduntil
*/
if (valuntil == NULL || *valuntil == '\0')
if (isnull)
retval = STATUS_OK;
else if (vuntil < GetCurrentTimestamp())
retval = STATUS_ERROR;
else
{
TimestampTz vuntil;
vuntil = DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
CStringGetDatum(valuntil),
ObjectIdGetDatum(InvalidOid),
Int32GetDatum(-1)));
if (vuntil < GetCurrentTimestamp())
retval = STATUS_ERROR;
else
retval = STATUS_OK;
}
retval = STATUS_OK;
}
if (port->hba->auth_method == uaMD5)

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.188 2009/06/24 13:39:42 mha Exp $
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.189 2009/08/29 19:26:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -29,9 +29,9 @@
#include "libpq/libpq.h"
#include "regex/regex.h"
#include "storage/fd.h"
#include "utils/flatfiles.h"
#include "utils/acl.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
@ -42,31 +42,21 @@
#define MAX_TOKEN 256
/* pre-parsed content of HBA config file */
/* pre-parsed content of HBA config file: list of HbaLine structs */
static List *parsed_hba_lines = NIL;
/*
* These variables hold the pre-parsed contents of the ident
* configuration files, as well as the flat auth file.
* Each is a list of sublists, one sublist for
* each (non-empty, non-comment) line of the file. Each sublist's
* first item is an integer line number (so we can give somewhat-useful
* location info in error messages). Remaining items are palloc'd strings,
* one string per token on the line. Note there will always be at least
* one token, since blank lines are not entered in the data structure.
* These variables hold the pre-parsed contents of the ident usermap
* configuration file. ident_lines is a list of sublists, one sublist for
* each (non-empty, non-comment) line of the file. The sublist items are
* palloc'd strings, one string per token on the line. Note there will always
* be at least one token, since blank lines are not entered in the data
* structure. ident_line_nums is an integer list containing the actual line
* number for each line represented in ident_lines.
*/
/* pre-parsed content of ident usermap file and corresponding line #s */
static List *ident_lines = NIL;
static List *ident_line_nums = NIL;
/* pre-parsed content of flat auth file and corresponding line #s */
static List *role_lines = NIL;
static List *role_line_nums = NIL;
/* sorted entries so we can do binary search lookups */
static List **role_sorted = NULL; /* sorted role list, for bsearch() */
static int role_length;
static void tokenize_file(const char *filename, FILE *file,
List **lines, List **line_nums);
@ -434,70 +424,28 @@ tokenize_file(const char *filename, FILE *file,
}
}
/*
* Compare two lines based on their role/member names.
*
* Used for bsearch() lookup.
*/
static int
role_bsearch_cmp(const void *role, const void *list)
{
char *role2 = linitial(*(List **) list);
return strcmp(role, role2);
}
/*
* Lookup a role name in the pg_auth file
*/
List **
get_role_line(const char *role)
{
/* On some versions of Solaris, bsearch of zero items dumps core */
if (role_length == 0)
return NULL;
return (List **) bsearch((void *) role,
(void *) role_sorted,
role_length,
sizeof(List *),
role_bsearch_cmp);
}
/*
* Does user belong to role?
*
* user is always the name given as the attempted login identifier.
* userid is the OID of the role given as the attempted login identifier.
* We check to see if it is a member of the specified role name.
*/
static bool
is_member(const char *user, const char *role)
is_member(Oid userid, const char *role)
{
List **line;
ListCell *line_item;
Oid roleid;
if ((line = get_role_line(user)) == NULL)
if (!OidIsValid(userid))
return false; /* if user not exist, say "no" */
/* A user always belongs to its own role */
if (strcmp(user, role) == 0)
return true;
roleid = get_roleid(role);
/*
* skip over the role name, password, valuntil, examine all the membership
* entries
*/
if (list_length(*line) < 4)
return false;
for_each_cell(line_item, lnext(lnext(lnext(list_head(*line)))))
{
if (strcmp((char *) lfirst(line_item), role) == 0)
return true;
}
if (!OidIsValid(roleid))
return false; /* if target role not exist, say "no" */
return false;
/* See if user is directly or indirectly a member of role */
return is_member_of_role(userid, roleid);
}
/*
@ -508,7 +456,7 @@ is_member(const char *user, const char *role)
* and so it doesn't matter that we clobber the stored hba info.
*/
static bool
check_role(const char *role, char *param_str)
check_role(const char *role, Oid roleid, char *param_str)
{
char *tok;
@ -518,7 +466,7 @@ check_role(const char *role, char *param_str)
{
if (tok[0] == '+')
{
if (is_member(role, tok + 1))
if (is_member(roleid, tok + 1))
return true;
}
else if (strcmp(tok, role) == 0 ||
@ -537,7 +485,7 @@ check_role(const char *role, char *param_str)
* and so it doesn't matter that we clobber the stored hba info.
*/
static bool
check_db(const char *dbname, const char *role, char *param_str)
check_db(const char *dbname, const char *role, Oid roleid, char *param_str)
{
char *tok;
@ -555,7 +503,7 @@ check_db(const char *dbname, const char *role, char *param_str)
else if (strcmp(tok, "samegroup\n") == 0 ||
strcmp(tok, "samerole\n") == 0)
{
if (is_member(role, dbname))
if (is_member(roleid, dbname))
return true;
}
else if (strcmp(tok, dbname) == 0)
@ -1106,9 +1054,13 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
static bool
check_hba(hbaPort *port)
{
Oid roleid;
ListCell *line;
HbaLine *hba;
/* Get the target role's OID. Note we do not error out for bad role. */
roleid = get_roleid(port->user_name);
foreach(line, parsed_hba_lines)
{
hba = (HbaLine *) lfirst(line);
@ -1177,10 +1129,11 @@ check_hba(hbaPort *port)
} /* != ctLocal */
/* Check database and role */
if (!check_db(port->database_name, port->user_name, hba->database))
if (!check_db(port->database_name, port->user_name, roleid,
hba->database))
continue;
if (!check_role(port->user_name, hba->role))
if (!check_role(port->user_name, roleid, hba->role))
continue;
/* Found a record that matched! */
@ -1200,58 +1153,6 @@ check_hba(hbaPort *port)
*/
}
/*
* Load role/password mapping file
*/
void
load_role(void)
{
char *filename;
FILE *role_file;
/* Discard any old data */
if (role_lines || role_line_nums)
free_lines(&role_lines, &role_line_nums);
if (role_sorted)
pfree(role_sorted);
role_sorted = NULL;
role_length = 0;
/* Read in the file contents */
filename = auth_getflatfilename();
role_file = AllocateFile(filename, "r");
if (role_file == NULL)
{
/* no complaint if not there */
if (errno != ENOENT)
ereport(LOG,
(errcode_for_file_access(),
errmsg("could not open file \"%s\": %m", filename)));
pfree(filename);
return;
}
tokenize_file(filename, role_file, &role_lines, &role_line_nums);
FreeFile(role_file);
pfree(filename);
/* create array for binary searching */
role_length = list_length(role_lines);
if (role_length)
{
int i = 0;
ListCell *line;
/* We assume the flat file was written already-sorted */
role_sorted = palloc(role_length * sizeof(List *));
foreach(line, role_lines)
role_sorted[i++] = lfirst(line);
}
}
/*
* Free the contents of a hba record
*/
@ -1613,7 +1514,7 @@ ident_syntax:
* as Postgres user "pgrole" according to usermap "usermap_name".
*
* Special case: Usermap NULL, equivalent to what was previously called
* "sameuser" or "samerole", don't look in the usermap
* "sameuser" or "samerole", means don't look in the usermap
* file. That's an implied map where "pgrole" must be identical to
* "ident_user" in order to be authorized.
*

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/pqsignal.c,v 1.45 2009/01/01 17:23:42 momjian Exp $
* $PostgreSQL: pgsql/src/backend/libpq/pqsignal.c,v 1.46 2009/08/29 19:26:51 tgl Exp $
*
* NOTES
* This shouldn't be in libpq, but the monitor and some other
@ -49,23 +49,23 @@
#ifdef HAVE_SIGPROCMASK
sigset_t UnBlockSig,
BlockSig,
AuthBlockSig;
StartupBlockSig;
#else
int UnBlockSig,
BlockSig,
AuthBlockSig;
StartupBlockSig;
#endif
/*
* Initialize BlockSig, UnBlockSig, and AuthBlockSig.
* Initialize BlockSig, UnBlockSig, and StartupBlockSig.
*
* BlockSig is the set of signals to block when we are trying to block
* signals. This includes all signals we normally expect to get, but NOT
* signals that should never be turned off.
*
* AuthBlockSig is the set of signals to block during authentication;
* it's essentially BlockSig minus SIGTERM, SIGQUIT, SIGALRM.
* StartupBlockSig is the set of signals to block during startup packet
* collection; it's essentially BlockSig minus SIGTERM, SIGQUIT, SIGALRM.
*
* UnBlockSig is the set of signals to block when we don't want to block
* signals (is this ever nonzero??)
@ -79,7 +79,7 @@ pqinitmask(void)
/* First set all signals, then clear some. */
sigfillset(&BlockSig);
sigfillset(&AuthBlockSig);
sigfillset(&StartupBlockSig);
/*
* Unmark those signals that should never be blocked. Some of these signal
@ -88,46 +88,46 @@ pqinitmask(void)
*/
#ifdef SIGTRAP
sigdelset(&BlockSig, SIGTRAP);
sigdelset(&AuthBlockSig, SIGTRAP);
sigdelset(&StartupBlockSig, SIGTRAP);
#endif
#ifdef SIGABRT
sigdelset(&BlockSig, SIGABRT);
sigdelset(&AuthBlockSig, SIGABRT);
sigdelset(&StartupBlockSig, SIGABRT);
#endif
#ifdef SIGILL
sigdelset(&BlockSig, SIGILL);
sigdelset(&AuthBlockSig, SIGILL);
sigdelset(&StartupBlockSig, SIGILL);
#endif
#ifdef SIGFPE
sigdelset(&BlockSig, SIGFPE);
sigdelset(&AuthBlockSig, SIGFPE);
sigdelset(&StartupBlockSig, SIGFPE);
#endif
#ifdef SIGSEGV
sigdelset(&BlockSig, SIGSEGV);
sigdelset(&AuthBlockSig, SIGSEGV);
sigdelset(&StartupBlockSig, SIGSEGV);
#endif
#ifdef SIGBUS
sigdelset(&BlockSig, SIGBUS);
sigdelset(&AuthBlockSig, SIGBUS);
sigdelset(&StartupBlockSig, SIGBUS);
#endif
#ifdef SIGSYS
sigdelset(&BlockSig, SIGSYS);
sigdelset(&AuthBlockSig, SIGSYS);
sigdelset(&StartupBlockSig, SIGSYS);
#endif
#ifdef SIGCONT
sigdelset(&BlockSig, SIGCONT);
sigdelset(&AuthBlockSig, SIGCONT);
sigdelset(&StartupBlockSig, SIGCONT);
#endif
/* Signals unique to Auth */
/* Signals unique to startup */
#ifdef SIGQUIT
sigdelset(&AuthBlockSig, SIGQUIT);
sigdelset(&StartupBlockSig, SIGQUIT);
#endif
#ifdef SIGTERM
sigdelset(&AuthBlockSig, SIGTERM);
sigdelset(&StartupBlockSig, SIGTERM);
#endif
#ifdef SIGALRM
sigdelset(&AuthBlockSig, SIGALRM);
sigdelset(&StartupBlockSig, SIGALRM);
#endif
#else
/* Set the signals we want. */
@ -139,7 +139,7 @@ pqinitmask(void)
sigmask(SIGINT) | sigmask(SIGUSR1) |
sigmask(SIGUSR2) | sigmask(SIGCHLD) |
sigmask(SIGWINCH) | sigmask(SIGFPE);
AuthBlockSig = sigmask(SIGHUP) |
StartupBlockSig = sigmask(SIGHUP) |
sigmask(SIGINT) | sigmask(SIGUSR1) |
sigmask(SIGUSR2) | sigmask(SIGCHLD) |
sigmask(SIGWINCH) | sigmask(SIGFPE);

View File

@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.592 2009/08/28 18:23:53 tgl Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.593 2009/08/29 19:26:51 tgl Exp $
*
* NOTES
*
@ -327,6 +327,7 @@ static void SIGHUP_handler(SIGNAL_ARGS);
static void pmdie(SIGNAL_ARGS);
static void reaper(SIGNAL_ARGS);
static void sigusr1_handler(SIGNAL_ARGS);
static void startup_die(SIGNAL_ARGS);
static void dummy_handler(SIGNAL_ARGS);
static void CleanupBackend(int pid, int exitstatus);
static void HandleChildCrash(int pid, int exitstatus, const char *procname);
@ -1561,7 +1562,8 @@ ProcessStartupPacket(Port *port, bool SSLdone)
if (proto == CANCEL_REQUEST_CODE)
{
processCancelRequest(port, buf);
return 127; /* XXX */
/* Not really an error, but we don't want to proceed further */
return STATUS_ERROR;
}
if (proto == NEGOTIATE_SSL_CODE && !SSLdone)
@ -1623,10 +1625,10 @@ retry1:
/*
* Now fetch parameters out of startup packet and save them into the Port
* structure. All data structures attached to the Port struct must be
* allocated in TopMemoryContext so that they won't disappear when we pass
* them to PostgresMain (see BackendRun). We need not worry about leaking
* this storage on failure, since we aren't in the postmaster process
* anymore.
* allocated in TopMemoryContext so that they will remain available in
* a running backend (even after PostmasterContext is destroyed). We need
* not worry about leaking this storage on failure, since we aren't in the
* postmaster process anymore.
*/
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
@ -2291,13 +2293,6 @@ reaper(SIGNAL_ARGS)
FatalError = false;
pmState = PM_RUN;
/*
* Load the flat authorization file into postmaster's cache. The
* startup process has recomputed this from the database contents,
* so we wait till it finishes before loading it.
*/
load_role();
/*
* Crank up the background writer, if we didn't do that already
* when we entered consistent recovery state. It doesn't matter
@ -3054,7 +3049,7 @@ BackendStartup(Port *port)
/* Close the postmaster's sockets */
ClosePostmasterPorts(false);
/* Perform additional initialization and client authentication */
/* Perform additional initialization and collect startup packet */
BackendInitialize(port);
/* And run the backend */
@ -3129,39 +3124,9 @@ report_fork_failure_to_client(Port *port, int errnum)
}
/*
* split_opts -- split a string of options and append it to an argv array
*
* NB: the string is destructively modified!
*
* Since no current POSTGRES arguments require any quoting characters,
* we can use the simple-minded tactic of assuming each set of space-
* delimited characters is a separate argv element.
*
* If you don't like that, well, we *used* to pass the whole option string
* as ONE argument to execl(), which was even less intelligent...
*/
static void
split_opts(char **argv, int *argcp, char *s)
{
while (s && *s)
{
while (isspace((unsigned char) *s))
++s;
if (*s == '\0')
break;
argv[(*argcp)++] = s;
while (*s && !isspace((unsigned char) *s))
++s;
if (*s)
*s++ = '\0';
}
}
/*
* BackendInitialize -- initialize an interactive (postmaster-child)
* backend process, and perform client authentication.
* backend process, and collect the client's startup packet.
*
* returns: nothing. Will not return at all if there's any failure.
*
@ -3183,13 +3148,14 @@ BackendInitialize(Port *port)
/*
* PreAuthDelay is a debugging aid for investigating problems in the
* authentication cycle: it can be set in postgresql.conf to allow time to
* attach to the newly-forked backend with a debugger. (See also the -W
* backend switch, which we allow clients to pass through PGOPTIONS, but
* attach to the newly-forked backend with a debugger. (See also
* PostAuthDelay, which we allow clients to pass through PGOPTIONS, but
* it is not honored until after authentication.)
*/
if (PreAuthDelay > 0)
pg_usleep(PreAuthDelay * 1000000L);
/* This flag will remain set until InitPostgres finishes authentication */
ClientAuthInProgress = true; /* limit visibility of log messages */
/* save process start time */
@ -3218,15 +3184,15 @@ BackendInitialize(Port *port)
#endif
/*
* We arrange for a simple exit(1) if we receive SIGTERM or SIGQUIT during
* any client authentication related communication. Otherwise the
* We arrange for a simple exit(1) if we receive SIGTERM or SIGQUIT
* or timeout while trying to collect the startup packet. Otherwise the
* postmaster cannot shutdown the database FAST or IMMED cleanly if a
* buggy client blocks a backend during authentication.
* buggy client fails to send the packet promptly.
*/
pqsignal(SIGTERM, authdie);
pqsignal(SIGQUIT, authdie);
pqsignal(SIGALRM, authdie);
PG_SETMASK(&AuthBlockSig);
pqsignal(SIGTERM, startup_die);
pqsignal(SIGQUIT, startup_die);
pqsignal(SIGALRM, startup_die);
PG_SETMASK(&StartupBlockSig);
/*
* Get the remote host name and port for logging and status display.
@ -3265,42 +3231,13 @@ BackendInitialize(Port *port)
port->remote_port = strdup(remote_port);
/*
* In EXEC_BACKEND case, we didn't inherit the contents of pg_hba.conf
* etcetera from the postmaster, and have to load them ourselves. Build
* the PostmasterContext (which didn't exist before, in this process) to
* contain the data.
*
* FIXME: [fork/exec] Ugh. Is there a way around this overhead?
*/
#ifdef EXEC_BACKEND
Assert(PostmasterContext == NULL);
PostmasterContext = AllocSetContextCreate(TopMemoryContext,
"Postmaster",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
MemoryContextSwitchTo(PostmasterContext);
if (!load_hba())
{
/*
* It makes no sense to continue if we fail to load the HBA file,
* since there is no way to connect to the database in this case.
*/
ereport(FATAL,
(errmsg("could not load pg_hba.conf")));
}
load_ident();
load_role();
#endif
/*
* Ready to begin client interaction. We will give up and exit(0) after a
* Ready to begin client interaction. We will give up and exit(1) after a
* time delay, so that a broken client can't hog a connection
* indefinitely. PreAuthDelay doesn't count against the time limit.
* indefinitely. PreAuthDelay and any DNS interactions above don't count
* against the time limit.
*/
if (!enable_sig_alarm(AuthenticationTimeout * 1000, false))
elog(FATAL, "could not set timer for authorization timeout");
elog(FATAL, "could not set timer for startup packet timeout");
/*
* Receive the startup packet (which might turn out to be a cancel request
@ -3308,6 +3245,10 @@ BackendInitialize(Port *port)
*/
status = ProcessStartupPacket(port, false);
/*
* Stop here if it was bad or a cancel packet. ProcessStartupPacket
* already did any appropriate error reporting.
*/
if (status != STATUS_OK)
proc_exit(0);
@ -3319,22 +3260,11 @@ BackendInitialize(Port *port)
update_process_title ? "authentication" : "");
/*
* Now perform authentication exchange.
*/
ClientAuthentication(port); /* might not return, if failure */
/*
* Done with authentication. Disable timeout, and prevent SIGTERM/SIGQUIT
* again until backend startup is complete.
* Disable the timeout, and prevent SIGTERM/SIGQUIT again.
*/
if (!disable_sig_alarm(false))
elog(FATAL, "could not disable timer for authorization timeout");
elog(FATAL, "could not disable timer for startup packet timeout");
PG_SETMASK(&BlockSig);
if (Log_connections)
ereport(LOG,
(errmsg("connection authorized: user=%s database=%s",
port->user_name, port->database_name)));
}
@ -3366,22 +3296,15 @@ BackendRun(Port *port)
TimestampDifference(0, port->SessionStartTime, &secs, &usecs);
srandom((unsigned int) (MyProcPid ^ usecs));
/* ----------------
/*
* Now, build the argv vector that will be given to PostgresMain.
*
* The layout of the command line is
* postgres [secure switches] -y databasename [insecure switches]
* where the switches after -y come from the client request.
*
* The maximum possible number of commandline arguments that could come
* from ExtraOptions or port->cmdline_options is (strlen + 1) / 2; see
* split_opts().
* ----------------
* from ExtraOptions is (strlen(ExtraOptions) + 1) / 2; see
* pg_split_opts().
*/
maxac = 10; /* for fixed args supplied below */
maxac = 5; /* for fixed args supplied below */
maxac += (strlen(ExtraOptions) + 1) / 2;
if (port->cmdline_options)
maxac += (strlen(port->cmdline_options) + 1) / 2;
av = (char **) MemoryContextAlloc(TopMemoryContext,
maxac * sizeof(char *));
@ -3390,41 +3313,21 @@ BackendRun(Port *port)
av[ac++] = "postgres";
/*
* Pass any backend switches specified with -o in the postmaster's own
* Pass any backend switches specified with -o on the postmaster's own
* command line. We assume these are secure. (It's OK to mangle
* ExtraOptions now, since we're safely inside a subprocess.)
*/
split_opts(av, &ac, ExtraOptions);
pg_split_opts(av, &ac, ExtraOptions);
/*
* Tell the backend it is being called from the postmaster, and which
* database to use. -y marks the end of secure switches.
* Tell the backend which database to use.
*/
av[ac++] = "-y";
av[ac++] = port->database_name;
/*
* Pass the (insecure) option switches from the connection request. (It's
* OK to mangle port->cmdline_options now.)
*/
if (port->cmdline_options)
split_opts(av, &ac, port->cmdline_options);
av[ac] = NULL;
Assert(ac < maxac);
/*
* Release postmaster's working memory context so that backend can recycle
* the space. Note this does not trash *MyProcPort, because ConnCreate()
* allocated that space with malloc() ... else we'd need to copy the Port
* data here. Also, subsidiary data such as the username isn't lost
* either; see ProcessStartupPacket().
*/
MemoryContextSwitchTo(TopMemoryContext);
MemoryContextDelete(PostmasterContext);
PostmasterContext = NULL;
/*
* Debug: print arguments being passed to backend
*/
@ -3437,7 +3340,11 @@ BackendRun(Port *port)
ereport(DEBUG3,
(errmsg_internal(")")));
ClientAuthInProgress = false; /* client_min_messages is active now */
/*
* Make sure we aren't in PostmasterContext anymore. (We can't delete it
* just yet, though, because InitPostgres will need the HBA data.)
*/
MemoryContextSwitchTo(TopMemoryContext);
return (PostgresMain(ac, av, port->user_name));
}
@ -3833,7 +3740,6 @@ SubPostmasterMain(int argc, char *argv[])
errmsg("out of memory")));
#endif
/* Check we got appropriate args */
if (argc < 3)
elog(FATAL, "invalid subpostmaster invocation");
@ -3901,7 +3807,7 @@ SubPostmasterMain(int argc, char *argv[])
#endif
/*
* Perform additional initialization and client authentication.
* Perform additional initialization and collect startup packet.
*
* We want to do this before InitProcess() for a couple of reasons: 1.
* so that we aren't eating up a PGPROC slot while waiting on the
@ -4074,13 +3980,6 @@ sigusr1_handler(SIGNAL_ARGS)
if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_CONSISTENT) &&
pmState == PM_RECOVERY)
{
/*
* Load the flat authorization file into postmaster's cache. The
* startup process won't have recomputed this from the database yet,
* so it may change following recovery.
*/
load_role();
/*
* Likewise, start other special children as needed.
*/
@ -4094,14 +3993,6 @@ sigusr1_handler(SIGNAL_ARGS)
pmState = PM_RECOVERY_CONSISTENT;
}
if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE))
{
/*
* Authorization file has changed.
*/
load_role();
}
if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER) &&
PgArchPID != 0)
{
@ -4144,6 +4035,20 @@ sigusr1_handler(SIGNAL_ARGS)
errno = save_errno;
}
/*
* Timeout or shutdown signal from postmaster while processing startup packet.
* Cleanup and exit(1).
*
* XXX: possible future improvement: try to send a message indicating
* why we are disconnecting. Problem is to be sure we don't block while
* doing so, nor mess up SSL initialization. In practice, if the client
* has wedged here, it probably couldn't do anything with the message anyway.
*/
static void
startup_die(SIGNAL_ARGS)
{
proc_exit(1);
}
/*
* Dummy signal handler

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.570 2009/08/28 18:23:53 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.571 2009/08/29 19:26:51 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@ -55,6 +55,7 @@
#include "parser/analyze.h"
#include "parser/parser.h"
#include "postmaster/autovacuum.h"
#include "postmaster/postmaster.h"
#include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h"
#include "storage/ipc.h"
@ -73,8 +74,13 @@
#include "mb/pg_wchar.h"
extern int optind;
extern char *optarg;
extern int optind;
#ifdef HAVE_INT_OPTRESET
extern int optreset; /* might not be declared by system headers */
#endif
/* ----------------
* global variables
@ -151,7 +157,10 @@ static CachedPlanSource *unnamed_stmt_psrc = NULL;
static MemoryContext unnamed_stmt_context = NULL;
static bool EchoQuery = false; /* default don't echo */
/* assorted command-line switches */
static const char *userDoption = NULL; /* -D switch */
static bool EchoQuery = false; /* -E switch */
/*
* people who want to use EOF should #define DONTUSENEWLINE in
@ -2481,6 +2490,15 @@ quickdie(SIGNAL_ARGS)
{
PG_SETMASK(&BlockSig);
/*
* If we're aborting out of client auth, don't risk trying to send
* anything to the client; we will likely violate the protocol,
* not to mention that we may have interrupted the guts of OpenSSL
* or some authentication library.
*/
if (ClientAuthInProgress && whereToSendOutput == DestRemote)
whereToSendOutput = DestNone;
/*
* Ideally this should be ereport(FATAL), but then we'd not get control
* back...
@ -2552,20 +2570,6 @@ die(SIGNAL_ARGS)
errno = save_errno;
}
/*
* Timeout or shutdown signal from postmaster during client authentication.
* Simply exit(1).
*
* XXX: possible future improvement: try to send a message indicating
* why we are disconnecting. Problem is to be sure we don't block while
* doing so, nor mess up the authentication message exchange.
*/
void
authdie(SIGNAL_ARGS)
{
proc_exit(1);
}
/*
* Query-cancel signal from postmaster: abort current transaction
* at soonest convenient time
@ -2646,6 +2650,9 @@ ProcessInterrupts(void)
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
/* As in quickdie, don't risk sending to client during auth */
if (ClientAuthInProgress && whereToSendOutput == DestRemote)
whereToSendOutput = DestNone;
if (IsAutoVacuumWorkerProcess())
ereport(FATAL,
(errcode(ERRCODE_ADMIN_SHUTDOWN),
@ -2661,7 +2668,14 @@ ProcessInterrupts(void)
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
if (cancel_from_timeout)
/* As in quickdie, don't risk sending to client during auth */
if (ClientAuthInProgress && whereToSendOutput == DestRemote)
whereToSendOutput = DestNone;
if (ClientAuthInProgress)
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling authentication due to timeout")));
else if (cancel_from_timeout)
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to statement timeout")));
@ -2840,116 +2854,55 @@ get_stats_option_name(const char *arg)
/* ----------------------------------------------------------------
* PostgresMain
* postgres main loop -- all backends, interactive or otherwise start here
* process_postgres_switches
* Parse command line arguments for PostgresMain
*
* argc/argv are the command line arguments to be used. (When being forked
* by the postmaster, these are not the original argv array of the process.)
* username is the (possibly authenticated) PostgreSQL user name to be used
* for the session.
* This is called twice, once for the "secure" options coming from the
* postmaster or command line, and once for the "insecure" options coming
* from the client's startup packet. The latter have the same syntax but
* may be restricted in what they can do.
*
* argv[0] is the program name either way.
*
* ctx is PGC_POSTMASTER for secure options, PGC_BACKEND for insecure options
* coming from the client, or PGC_SUSET for insecure options coming from
* a superuser client.
*
* Returns the database name extracted from the command line, if any.
* ----------------------------------------------------------------
*/
int
PostgresMain(int argc, char *argv[], const char *username)
static const char *
process_postgres_switches(int argc, char *argv[], GucContext ctx)
{
int flag;
const char *dbname = NULL;
char *userDoption = NULL;
bool secure;
const char *dbname;
const char *argv0 = argv[0];
bool secure = (ctx == PGC_POSTMASTER);
int errs = 0;
int debug_flag = -1; /* -1 means not given */
List *guc_names = NIL; /* for SUSET options */
List *guc_values = NIL;
GucContext ctx;
GucSource gucsource;
bool am_superuser;
int firstchar;
char stack_base;
StringInfoData input_message;
sigjmp_buf local_sigjmp_buf;
volatile bool send_ready_for_query = true;
int flag;
#define PendingConfigOption(name,val) \
(guc_names = lappend(guc_names, pstrdup(name)), \
guc_values = lappend(guc_values, pstrdup(val)))
/*
* initialize globals (already done if under postmaster, but not if
* standalone; cheap enough to do over)
*/
MyProcPid = getpid();
MyStartTime = time(NULL);
/*
* Fire up essential subsystems: error and memory management
*
* If we are running under the postmaster, this is done already.
*/
if (!IsUnderPostmaster)
MemoryContextInit();
set_ps_display("startup", false);
SetProcessingMode(InitProcessing);
/* Set up reference point for stack depth checking */
stack_base_ptr = &stack_base;
/* Compute paths, if we didn't inherit them from postmaster */
if (my_exec_path[0] == '\0')
if (secure)
{
if (find_my_exec(argv[0], my_exec_path) < 0)
elog(FATAL, "%s: could not locate my own executable path",
argv[0]);
gucsource = PGC_S_ARGV; /* switches came from command line */
/* Ignore the initial --single argument, if present */
if (argc > 1 && strcmp(argv[1], "--single") == 0)
{
argv++;
argc--;
}
}
if (pkglib_path[0] == '\0')
get_pkglib_path(my_exec_path, pkglib_path);
/*
* Set default values for command-line options.
*/
EchoQuery = false;
if (!IsUnderPostmaster)
InitializeGUCOptions();
/* ----------------
* parse command line arguments
*
* There are now two styles of command line layout for the backend:
*
* For interactive use (not started from postmaster) the format is
* postgres [switches] [databasename]
* If the databasename is omitted it is taken to be the user name.
*
* When started from the postmaster, the format is
* postgres [secure switches] -y databasename [insecure switches]
* Switches appearing after -y came from the client (via "options"
* field of connection request). For security reasons we restrict
* what these switches can do.
* ----------------
*/
/* Ignore the initial --single argument, if present */
if (argc > 1 && strcmp(argv[1], "--single") == 0)
else
{
argv++;
argc--;
gucsource = PGC_S_CLIENT; /* switches came from client */
}
/* all options are allowed until '-y' */
secure = true;
ctx = PGC_POSTMASTER;
gucsource = PGC_S_ARGV; /* initial switches came from command line */
/*
* Parse command-line options. CAUTION: keep this in sync with
* postmaster/postmaster.c (the option sets should not conflict) and with
* the common help() function in main/main.c.
*/
while ((flag = getopt(argc, argv, "A:B:c:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:v:W:y:-:")) != -1)
while ((flag = getopt(argc, argv, "A:B:c:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:v:W:-:")) != -1)
{
switch (flag)
{
@ -2963,11 +2916,11 @@ PostgresMain(int argc, char *argv[], const char *username)
case 'D':
if (secure)
userDoption = optarg;
userDoption = strdup(optarg);
break;
case 'd':
debug_flag = atoi(optarg);
set_debug_options(atoi(optarg), ctx, gucsource);
break;
case 'E':
@ -3042,16 +2995,7 @@ PostgresMain(int argc, char *argv[], const char *username)
break;
case 's':
/*
* Since log options are SUSET, we need to postpone unless
* still in secure context
*/
if (ctx == PGC_BACKEND)
PendingConfigOption("log_statement_stats", "true");
else
SetConfigOption("log_statement_stats", "true",
ctx, gucsource);
SetConfigOption("log_statement_stats", "true", ctx, gucsource);
break;
case 'T':
@ -3063,12 +3007,7 @@ PostgresMain(int argc, char *argv[], const char *username)
const char *tmp = get_stats_option_name(optarg);
if (tmp)
{
if (ctx == PGC_BACKEND)
PendingConfigOption(tmp, "true");
else
SetConfigOption(tmp, "true", ctx, gucsource);
}
SetConfigOption(tmp, "true", ctx, gucsource);
else
errs++;
break;
@ -3090,23 +3029,6 @@ PostgresMain(int argc, char *argv[], const char *username)
SetConfigOption("post_auth_delay", optarg, ctx, gucsource);
break;
case 'y':
/*
* y - special flag passed if backend was forked by a
* postmaster.
*/
if (secure)
{
dbname = strdup(optarg);
secure = false; /* subsequent switches are NOT secure */
ctx = PGC_BACKEND;
gucsource = PGC_S_CLIENT;
}
break;
case 'c':
case '-':
{
@ -3127,15 +3049,7 @@ PostgresMain(int argc, char *argv[], const char *username)
errmsg("-c %s requires a value",
optarg)));
}
/*
* If a SUSET option, must postpone evaluation, unless we
* are still reading secure switches.
*/
if (ctx == PGC_BACKEND && IsSuperuserConfigOption(name))
PendingConfigOption(name, value);
else
SetConfigOption(name, value, ctx, gucsource);
SetConfigOption(name, value, ctx, gucsource);
free(name);
if (value)
free(value);
@ -3149,29 +3063,120 @@ PostgresMain(int argc, char *argv[], const char *username)
}
/*
* Process any additional GUC variable settings passed in startup packet.
* These are handled exactly like command-line variables.
* Should be no more arguments except an optional database name, and
* that's only in the secure case.
*/
if (MyProcPort != NULL)
if (errs || argc - optind > 1 || (argc != optind && !secure))
{
ListCell *gucopts = list_head(MyProcPort->guc_options);
/* spell the error message a bit differently depending on context */
if (IsUnderPostmaster)
ereport(FATAL,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid command-line arguments for server process"),
errhint("Try \"%s --help\" for more information.", argv0)));
else
ereport(FATAL,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("%s: invalid command-line arguments",
argv0),
errhint("Try \"%s --help\" for more information.", argv0)));
}
while (gucopts)
{
char *name;
char *value;
if (argc - optind == 1)
dbname = strdup(argv[optind]);
else
dbname = NULL;
name = lfirst(gucopts);
gucopts = lnext(gucopts);
/*
* Reset getopt(3) library so that it will work correctly in subprocesses
* or when this function is called a second time with another array.
*/
optind = 1;
#ifdef HAVE_INT_OPTRESET
optreset = 1; /* some systems need this too */
#endif
value = lfirst(gucopts);
gucopts = lnext(gucopts);
return dbname;
}
if (IsSuperuserConfigOption(name))
PendingConfigOption(name, value);
else
SetConfigOption(name, value, PGC_BACKEND, PGC_S_CLIENT);
}
/* ----------------------------------------------------------------
* PostgresMain
* postgres main loop -- all backends, interactive or otherwise start here
*
* argc/argv are the command line arguments to be used. (When being forked
* by the postmaster, these are not the original argv array of the process.)
* username is the (possibly authenticated) PostgreSQL user name to be used
* for the session.
* ----------------------------------------------------------------
*/
int
PostgresMain(int argc, char *argv[], const char *username)
{
const char *dbname;
bool am_superuser;
GucContext ctx;
int firstchar;
char stack_base;
StringInfoData input_message;
sigjmp_buf local_sigjmp_buf;
volatile bool send_ready_for_query = true;
/*
* Initialize globals (already done if under postmaster, but not if
* standalone).
*/
if (!IsUnderPostmaster)
{
MyProcPid = getpid();
MyStartTime = time(NULL);
}
/*
* Fire up essential subsystems: error and memory management
*
* If we are running under the postmaster, this is done already.
*/
if (!IsUnderPostmaster)
MemoryContextInit();
SetProcessingMode(InitProcessing);
/* Set up reference point for stack depth checking */
stack_base_ptr = &stack_base;
/* Compute paths, if we didn't inherit them from postmaster */
if (my_exec_path[0] == '\0')
{
if (find_my_exec(argv[0], my_exec_path) < 0)
elog(FATAL, "%s: could not locate my own executable path",
argv[0]);
}
if (pkglib_path[0] == '\0')
get_pkglib_path(my_exec_path, pkglib_path);
/*
* Set default values for command-line options.
*/
if (!IsUnderPostmaster)
InitializeGUCOptions();
/*
* Parse command-line options.
*/
dbname = process_postgres_switches(argc, argv, PGC_POSTMASTER);
/* Must have gotten a database name, or have a default (the username) */
if (dbname == NULL)
{
dbname = username;
if (dbname == NULL)
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("%s: no database nor user name specified",
argv[0])));
}
/* Acquire configuration parameters, unless inherited from postmaster */
@ -3185,9 +3190,6 @@ PostgresMain(int argc, char *argv[], const char *username)
pg_timezone_abbrev_initialize();
}
if (PostAuthDelay)
pg_usleep(PostAuthDelay * 1000000L);
/*
* You might expect to see a setsid() call here, but it's not needed,
* because if we are under a postmaster then BackendInitialize() did it.
@ -3254,38 +3256,10 @@ PostgresMain(int argc, char *argv[], const char *username)
if (IsUnderPostmaster)
{
/* noninteractive case: nothing should be left after switches */
if (errs || argc != optind || dbname == NULL)
{
ereport(FATAL,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid command-line arguments for server process"),
errhint("Try \"%s --help\" for more information.", argv[0])));
}
BaseInit();
}
else
{
/* interactive case: database name can be last arg on command line */
if (errs || argc - optind > 1)
{
ereport(FATAL,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("%s: invalid command-line arguments",
argv[0]),
errhint("Try \"%s --help\" for more information.", argv[0])));
}
else if (argc - optind == 1)
dbname = argv[optind];
else if ((dbname = username) == NULL)
{
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("%s: no database nor user name specified",
argv[0])));
}
/*
* Validate we have been given a reasonable-looking DataDir (if under
* postmaster, assume postmaster did this already).
@ -3330,6 +3304,9 @@ PostgresMain(int argc, char *argv[], const char *username)
InitProcess();
#endif
/* We need to allow SIGINT, etc during the initial transaction */
PG_SETMASK(&UnBlockSig);
/*
* General initialization.
*
@ -3337,37 +3314,87 @@ PostgresMain(int argc, char *argv[], const char *username)
* it inside InitPostgres() instead. In particular, anything that
* involves database access should be there, not here.
*/
ereport(DEBUG3,
(errmsg_internal("InitPostgres")));
am_superuser = InitPostgres(dbname, InvalidOid, username, NULL);
/*
* If the PostmasterContext is still around, recycle the space; we don't
* need it anymore after InitPostgres completes. Note this does not trash
* *MyProcPort, because ConnCreate() allocated that space with malloc()
* ... else we'd need to copy the Port data first. Also, subsidiary data
* such as the username isn't lost either; see ProcessStartupPacket().
*/
if (PostmasterContext)
{
MemoryContextDelete(PostmasterContext);
PostmasterContext = NULL;
}
SetProcessingMode(NormalProcessing);
set_ps_display("startup", false);
/*
* Now that we know if client is a superuser, we can try to apply SUSET
* GUC options that came from the client.
* Now that we know if client is a superuser, we can try to apply any
* command-line options passed in the startup packet.
*/
ctx = am_superuser ? PGC_SUSET : PGC_USERSET;
ctx = am_superuser ? PGC_SUSET : PGC_BACKEND;
if (debug_flag >= 0)
set_debug_options(debug_flag, ctx, PGC_S_CLIENT);
if (guc_names != NIL)
if (MyProcPort != NULL &&
MyProcPort->cmdline_options != NULL)
{
ListCell *namcell,
*valcell;
/*
* The maximum possible number of commandline arguments that could
* come from MyProcPort->cmdline_options is (strlen + 1) / 2; see
* pg_split_opts().
*/
char **av;
int maxac;
int ac;
forboth(namcell, guc_names, valcell, guc_values)
maxac = 2 + (strlen(MyProcPort->cmdline_options) + 1) / 2;
av = (char **) palloc(maxac * sizeof(char *));
ac = 0;
av[ac++] = argv[0];
/* Note this mangles MyProcPort->cmdline_options */
pg_split_opts(av, &ac, MyProcPort->cmdline_options);
av[ac] = NULL;
Assert(ac < maxac);
(void) process_postgres_switches(ac, av, ctx);
}
/*
* Process any additional GUC variable settings passed in startup packet.
* These are handled exactly like command-line variables.
*/
if (MyProcPort != NULL)
{
ListCell *gucopts = list_head(MyProcPort->guc_options);
while (gucopts)
{
char *name = (char *) lfirst(namcell);
char *value = (char *) lfirst(valcell);
char *name;
char *value;
name = lfirst(gucopts);
gucopts = lnext(gucopts);
value = lfirst(gucopts);
gucopts = lnext(gucopts);
SetConfigOption(name, value, ctx, PGC_S_CLIENT);
pfree(name);
pfree(value);
}
}
/* Apply PostAuthDelay as soon as we've read all options */
if (PostAuthDelay > 0)
pg_usleep(PostAuthDelay * 1000000L);
/*
* Now all GUC states are fully set up. Report them to client if
* appropriate.
@ -3514,8 +3541,6 @@ PostgresMain(int argc, char *argv[], const char *username)
/* We can now handle ereport(ERROR) */
PG_exception_stack = &local_sigjmp_buf;
PG_SETMASK(&UnBlockSig);
if (!ignore_till_sync)
send_ready_for_query = true; /* initially, or after error */

View File

@ -23,7 +23,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.37 2009/08/12 20:53:30 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.38 2009/08/29 19:26:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -824,11 +824,6 @@ AtEOXact_UpdateFlatFiles(bool isCommit)
heap_close(mrel, NoLock);
}
/*
* Signal the postmaster to reload its caches.
*/
SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
/*
* Force synchronous commit, to minimize the window between changing the
* flat files on-disk and marking the transaction committed. It's not

View File

@ -8,13 +8,14 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.194 2009/08/12 20:53:30 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.195 2009/08/29 19:26:51 tgl Exp $
*
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
@ -27,6 +28,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_database.h"
#include "catalog/pg_tablespace.h"
#include "libpq/auth.h"
#include "libpq/libpq-be.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
@ -54,6 +56,7 @@
static HeapTuple GetDatabaseTuple(const char *dbname);
static HeapTuple GetDatabaseTupleByOid(Oid dboid);
static void PerformAuthentication(Port *port);
static void CheckMyDatabase(const char *name, bool am_superuser);
static void InitCommunication(void);
static void ShutdownPostgres(int code, Datum arg);
@ -158,6 +161,66 @@ GetDatabaseTupleByOid(Oid dboid)
}
/*
* PerformAuthentication -- authenticate a remote client
*
* returns: nothing. Will not return at all if there's any failure.
*/
static void
PerformAuthentication(Port *port)
{
/* This should be set already, but let's make sure */
ClientAuthInProgress = true; /* limit visibility of log messages */
/*
* In EXEC_BACKEND case, we didn't inherit the contents of pg_hba.conf
* etcetera from the postmaster, and have to load them ourselves. Note
* we are loading them into the startup transaction's memory context,
* not PostmasterContext, but that shouldn't matter.
*
* FIXME: [fork/exec] Ugh. Is there a way around this overhead?
*/
#ifdef EXEC_BACKEND
if (!load_hba())
{
/*
* It makes no sense to continue if we fail to load the HBA file,
* since there is no way to connect to the database in this case.
*/
ereport(FATAL,
(errmsg("could not load pg_hba.conf")));
}
load_ident();
#endif
/*
* Set up a timeout in case a buggy or malicious client fails to respond
* during authentication. Since we're inside a transaction and might do
* database access, we have to use the statement_timeout infrastructure.
*/
if (!enable_sig_alarm(AuthenticationTimeout * 1000, true))
elog(FATAL, "could not set timer for authorization timeout");
/*
* Now perform authentication exchange.
*/
ClientAuthentication(port); /* might not return, if failure */
/*
* Done with authentication. Disable the timeout, and log if needed.
*/
if (!disable_sig_alarm(true))
elog(FATAL, "could not disable timer for authorization timeout");
if (Log_connections)
ereport(LOG,
(errmsg("connection authorized: user=%s database=%s",
port->user_name, port->database_name)));
ClientAuthInProgress = false; /* client_min_messages is active now */
}
/*
* CheckMyDatabase -- fetch information from the pg_database entry for our DB
*/
@ -329,6 +392,38 @@ InitCommunication(void)
}
/*
* pg_split_opts -- split a string of options and append it to an argv array
*
* NB: the input string is destructively modified! Also, caller is responsible
* for ensuring the argv array is large enough. The maximum possible number
* of arguments added by this routine is (strlen(optstr) + 1) / 2.
*
* Since no current POSTGRES arguments require any quoting characters,
* we can use the simple-minded tactic of assuming each set of space-
* delimited characters is a separate argv element.
*
* If you don't like that, well, we *used* to pass the whole option string
* as ONE argument to execl(), which was even less intelligent...
*/
void
pg_split_opts(char **argv, int *argcp, char *optstr)
{
while (*optstr)
{
while (isspace((unsigned char) *optstr))
optstr++;
if (*optstr == '\0')
break;
argv[(*argcp)++] = optstr;
while (*optstr && !isspace((unsigned char) *optstr))
optstr++;
if (*optstr)
*optstr++ = '\0';
}
}
/*
* Early initialization of a backend (either standalone or under postmaster).
* This happens even before InitPostgres.
@ -388,6 +483,8 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
char *fullpath;
char dbname[NAMEDATALEN];
elog(DEBUG3, "InitPostgres");
/*
* Add my PGPROC struct to the ProcArray.
*
@ -599,7 +696,8 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
RelationCacheInitializePhase3();
/*
* Figure out our postgres user id, and see if we are a superuser.
* Perform client authentication if necessary, then figure out our
* postgres user id, and see if we are a superuser.
*
* In standalone mode and in the autovacuum process, we use a fixed id,
* otherwise we figure it out from the authenticated user name.
@ -623,6 +721,8 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
else
{
/* normal multiuser case */
Assert(MyProcPort != NULL);
PerformAuthentication(MyProcPort);
InitializeSessionUserId(username);
am_superuser = superuser();
}

View File

@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.511 2009/08/24 20:08:32 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.512 2009/08/29 19:26:51 tgl Exp $
*
*--------------------------------------------------------------------
*/
@ -4635,7 +4635,8 @@ set_config_option(const char *name, const char *value,
if (IsUnderPostmaster)
return true;
}
else if (context != PGC_BACKEND && context != PGC_POSTMASTER)
else if (context != PGC_POSTMASTER && context != PGC_BACKEND &&
source != PGC_S_CLIENT)
{
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
@ -5243,22 +5244,6 @@ GetConfigOptionResetString(const char *name)
return NULL;
}
/*
* Detect whether the given configuration option can only be set by
* a superuser.
*/
bool
IsSuperuserConfigOption(const char *name)
{
struct config_generic *record;
record = find_option(name, false, ERROR);
/* On an unrecognized name, don't error, just return false. */
if (record == NULL)
return false;
return (record->context == PGC_SUSET);
}
/*
* GUC_complaint_elevel

View File

@ -4,7 +4,7 @@
* Interface to hba.c
*
*
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.56 2009/06/11 14:49:11 momjian Exp $
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.57 2009/08/29 19:26:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -61,12 +61,11 @@ typedef struct
bool include_realm;
} HbaLine;
/* kluge to avoid including libpq/libpq-be.h here */
typedef struct Port hbaPort;
extern List **get_role_line(const char *role);
extern bool load_hba(void);
extern void load_ident(void);
extern void load_role(void);
extern int hba_getauthmethod(hbaPort *port);
extern bool read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
Oid *dbtablespace, TransactionId *dbfrozenxid);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/libpq/pqsignal.h,v 1.33 2009/01/01 17:23:59 momjian Exp $
* $PostgreSQL: pgsql/src/include/libpq/pqsignal.h,v 1.34 2009/08/29 19:26:51 tgl Exp $
*
* NOTES
* This shouldn't be in libpq, but the monitor and some other
@ -23,13 +23,13 @@
#ifdef HAVE_SIGPROCMASK
extern sigset_t UnBlockSig,
BlockSig,
AuthBlockSig;
StartupBlockSig;
#define PG_SETMASK(mask) sigprocmask(SIG_SETMASK, mask, NULL)
#else
extern int UnBlockSig,
BlockSig,
AuthBlockSig;
StartupBlockSig;
#ifndef WIN32
#define PG_SETMASK(mask) sigsetmask(*((int*)(mask)))

View File

@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.212 2009/08/12 20:53:30 tgl Exp $
* $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.213 2009/08/29 19:26:51 tgl Exp $
*
* NOTES
* some of the information in this file should be moved to other files.
@ -323,6 +323,7 @@ extern ProcessingMode Mode;
*****************************************************************************/
/* in utils/init/postinit.c */
extern void pg_split_opts(char **argv, int *argcp, char *optstr);
extern bool InitPostgres(const char *in_dbname, Oid dboid, const char *username,
char *out_dbname);
extern void BaseInit(void);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/pmsignal.h,v 1.25 2009/06/11 14:49:12 momjian Exp $
* $PostgreSQL: pgsql/src/include/storage/pmsignal.h,v 1.26 2009/08/29 19:26:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -25,7 +25,6 @@ typedef enum
PMSIGNAL_RECOVERY_STARTED, /* recovery has started */
PMSIGNAL_RECOVERY_CONSISTENT, /* recovery has reached consistent
* state */
PMSIGNAL_PASSWORD_CHANGE, /* pg_auth file has changed */
PMSIGNAL_WAKEN_ARCHIVER, /* send a NOTIFY signal to xlog archiver */
PMSIGNAL_ROTATE_LOGFILE, /* send SIGUSR1 to syslogger to rotate logfile */
PMSIGNAL_START_AUTOVAC_LAUNCHER, /* start an autovacuum launcher */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.98 2009/06/11 14:49:12 momjian Exp $
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.99 2009/08/29 19:26:52 tgl Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
@ -58,7 +58,6 @@ extern bool assign_max_stack_depth(int newval, bool doit, GucSource source);
extern void die(SIGNAL_ARGS);
extern void quickdie(SIGNAL_ARGS);
extern void authdie(SIGNAL_ARGS);
extern void StatementCancelHandler(SIGNAL_ARGS);
extern void FloatExceptionHandler(SIGNAL_ARGS);
extern void prepare_for_client_read(void);

View File

@ -7,7 +7,7 @@
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.102 2009/06/11 14:49:13 momjian Exp $
* $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.103 2009/08/29 19:26:52 tgl Exp $
*--------------------------------------------------------------------
*/
#ifndef GUC_H
@ -247,7 +247,6 @@ extern void EmitWarningsOnPlaceholders(const char *className);
extern const char *GetConfigOption(const char *name);
extern const char *GetConfigOptionResetString(const char *name);
extern bool IsSuperuserConfigOption(const char *name);
extern void ProcessConfigFile(GucContext context);
extern void InitializeGUCOptions(void);
extern bool SelectConfigFiles(const char *userDoption, const char *progname);