#include "libpq/ip.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
+#include "miscadmin.h"
 #include "storage/ipc.h"
 
+
 /*----------------------------------------------------------------
  * Global authentication functions
  *----------------------------------------------------------------
                                 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
                sendAuthRequest(port, AUTH_REQ_OK);
        else
                auth_failed(port, status);
+
+       /* Done with authentication, so we should turn off immediate interrupts */
+       ImmediateInterruptOK = false;
 }
 
 
        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;
 {
        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:
 
 #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;
+
+       /*
+        * 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;
 
-       if ((line = get_role_line(role)) == NULL)
-               return STATUS_ERROR;
+       /* Get role info from pg_authid */
+       roleTup = SearchSysCache(AUTHNAME,
+                                                        PointerGetDatum(role),
+                                                        0, 0, 0);
+       if (!HeapTupleIsValid(roleTup))
+               return STATUS_ERROR;                                    /* no such user */
 
-       /* Skip over rolename */
-       token = list_head(*line);
-       if (token)
-               token = lnext(token);
-       if (token)
+       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);
+
+       datum = SysCacheGetAttr(AUTHNAME, roleTup,
+                                                       Anum_pg_authid_rolvaliduntil, &isnull);
+       if (!isnull)
+               vuntil = DatumGetTimestampTz(datum);
 
-       if (shadow_pass == NULL || *shadow_pass == '\0')
-               return STATUS_ERROR;
+       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
        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)
 
 #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))
 
 #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);
        }
 }
 
-/*
- * 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);
 }
 
 /*
  * 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;
 
        {
                if (tok[0] == '+')
                {
-                       if (is_member(role, tok + 1))
+                       if (is_member(roleid, tok + 1))
                                return true;
                }
                else if (strcmp(tok, role) == 0 ||
  * 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;
 
                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)
 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);
                }                                               /* != 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! */
         */
 }
 
-
-/*
- *      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
  */
  *     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.
  *
 
 #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??)
 
        /* 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
         */
 #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. */
                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);
 
 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);
        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)
        /*
         * 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);
 
                        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
                /* 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 */
 }
 
 
-/*
- * 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.
  *
        /*
         * 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 */
 #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.
        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
         */
        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);
 
                                        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)));
 }
 
 
        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 *));
        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
         */
        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));
 }
                                 errmsg("out of memory")));
 #endif
 
-
        /* Check we got appropriate args */
        if (argc < 3)
                elog(FATAL, "invalid subpostmaster invocation");
 #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
        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.
                 */
                pmState = PM_RECOVERY_CONSISTENT;
        }
 
-       if (CheckPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE))
-       {
-               /*
-                * Authorization file has changed.
-                */
-               load_role();
-       }
-
        if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER) &&
                PgArchPID != 0)
        {
        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
 
 #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"
 #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
 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
 {
        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...
        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
                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),
                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")));
 
 
 /* ----------------------------------------------------------------
- * 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;
-
-#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;
+       int                     flag;
 
-       /* 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 */
 
-       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)
+               /* Ignore the initial --single argument, if present */
+               if (argc > 1 && strcmp(argv[1], "--single") == 0)
+               {
+                       argv++;
+                       argc--;
+               }
+       }
+       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)
                {
 
                        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':
                                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':
                                        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;
                                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 '-':
                                {
                                                                         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);
        }
 
        /*
-        * 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 */
                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.
 
        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).
        InitProcess();
 #endif
 
+       /* We need to allow SIGINT, etc during the initial transaction */
+       PG_SETMASK(&UnBlockSig);
+
        /*
         * General initialization.
         *
         * 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 (MyProcPort != NULL &&
+               MyProcPort->cmdline_options != NULL)
+       {
+               /*
+                * 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;
+
+               maxac = 2 + (strlen(MyProcPort->cmdline_options) + 1) / 2;
+
+               av = (char **) palloc(maxac * sizeof(char *));
+               ac = 0;
+
+               av[ac++] = argv[0];
 
-       if (debug_flag >= 0)
-               set_debug_options(debug_flag, ctx, PGC_S_CLIENT);
+               /* Note this mangles MyProcPort->cmdline_options */
+               pg_split_opts(av, &ac, MyProcPort->cmdline_options);
 
-       if (guc_names != NIL)
+               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   *namcell,
-                                  *valcell;
+               ListCell   *gucopts = list_head(MyProcPort->guc_options);
 
-               forboth(namcell, guc_names, valcell, guc_values)
+               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.
        /* 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 */
 
 
                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
 
  */
 #include "postgres.h"
 
+#include <ctype.h>
 #include <fcntl.h>
 #include <unistd.h>
 
 #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"
 
 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);
 }
 
 
+/*
+ * 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
  */
 }
 
 
+/*
+ * 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.
        char       *fullpath;
        char            dbname[NAMEDATALEN];
 
+       elog(DEBUG3, "InitPostgres");
+
        /*
         * Add my PGPROC struct to the ProcArray.
         *
        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.
        else
        {
                /* normal multiuser case */
+               Assert(MyProcPort != NULL);
+               PerformAuthentication(MyProcPort);
                InitializeSessionUserId(username);
                am_superuser = superuser();
        }
 
                                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),
        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
 
        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);
 
 #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)))
 
  *****************************************************************************/
 
 /* 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);
 
        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 */
 
 
 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);
 
 
 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);