]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/init/miscinit.c
Perform an immediate shutdown if the postmaster.pid file is removed.
[postgresql] / src / backend / utils / init / miscinit.c
index 24ca97d55c73ab5072de0bc29d629fce2145c769..fb3cb6eb3d5ab53863963cc583b18f26618fe0db 100644 (file)
@@ -3,7 +3,7 @@
  * miscinit.c
  *       miscellaneous initialization support stuff
  *
- * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
 
 #include <sys/param.h>
 #include <signal.h>
+#include <time.h>
 #include <sys/file.h>
 #include <sys/stat.h>
 #include <sys/time.h>
+#include <sys/types.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <grp.h>
@@ -37,6 +39,7 @@
 #include "postmaster/postmaster.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
+#include "storage/latch.h"
 #include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
@@ -53,13 +56,14 @@ ProcessingMode Mode = InitProcessing;
 /* List of lock files to be removed at proc exit */
 static List *lock_files = NIL;
 
+static Latch LocalLatchData;
 
 /* ----------------------------------------------------------------
  *             ignoring system indexes support stuff
  *
  * NOTE: "ignoring system indexes" means we do not use the system indexes
  * for lookups (either in hardwired catalog accesses or in planner-generated
- * plans).     We do, however, still update the indexes when a catalog
+ * plans).  We do, however, still update the indexes when a catalog
  * modification is made.
  * ----------------------------------------------------------------
  */
@@ -117,77 +121,6 @@ ChangeToDataDir(void)
                                                DataDir)));
 }
 
-/*
- * If the given pathname isn't already absolute, make it so, interpreting
- * it relative to the current working directory.
- *
- * Also canonicalizes the path.  The result is always a malloc'd copy.
- *
- * Note: interpretation of relative-path arguments during postmaster startup
- * should happen before doing ChangeToDataDir(), else the user will probably
- * not like the results.
- */
-char *
-make_absolute_path(const char *path)
-{
-       char       *new;
-
-       /* Returning null for null input is convenient for some callers */
-       if (path == NULL)
-               return NULL;
-
-       if (!is_absolute_path(path))
-       {
-               char       *buf;
-               size_t          buflen;
-
-               buflen = MAXPGPATH;
-               for (;;)
-               {
-                       buf = malloc(buflen);
-                       if (!buf)
-                               ereport(FATAL,
-                                               (errcode(ERRCODE_OUT_OF_MEMORY),
-                                                errmsg("out of memory")));
-
-                       if (getcwd(buf, buflen))
-                               break;
-                       else if (errno == ERANGE)
-                       {
-                               free(buf);
-                               buflen *= 2;
-                               continue;
-                       }
-                       else
-                       {
-                               free(buf);
-                               elog(FATAL, "could not get current working directory: %m");
-                       }
-               }
-
-               new = malloc(strlen(buf) + strlen(path) + 2);
-               if (!new)
-                       ereport(FATAL,
-                                       (errcode(ERRCODE_OUT_OF_MEMORY),
-                                        errmsg("out of memory")));
-               sprintf(new, "%s/%s", buf, path);
-               free(buf);
-       }
-       else
-       {
-               new = strdup(path);
-               if (!new)
-                       ereport(FATAL,
-                                       (errcode(ERRCODE_OUT_OF_MEMORY),
-                                        errmsg("out of memory")));
-       }
-
-       /* Make sure punctuation is canonical, too */
-       canonicalize_path(new);
-
-       return new;
-}
-
 
 /* ----------------------------------------------------------------
  *     User ID state
@@ -231,6 +164,106 @@ static int        SecurityRestrictionContext = 0;
 /* We also remember if a SET ROLE is currently active */
 static bool SetRoleIsActive = false;
 
+/*
+ * Initialize the basic environment for a postmaster child
+ *
+ * Should be called as early as possible after the child's startup.
+ */
+void
+InitPostmasterChild(void)
+{
+       IsUnderPostmaster = true;       /* we are a postmaster subprocess now */
+
+       MyProcPid = getpid();           /* reset MyProcPid */
+
+       MyStartTime = time(NULL);       /* set our start time in case we call elog */
+
+       /*
+        * make sure stderr is in binary mode before anything can possibly be
+        * written to it, in case it's actually the syslogger pipe, so the pipe
+        * chunking protocol isn't disturbed. Non-logpipe data gets translated on
+        * redirection (e.g. via pg_ctl -l) anyway.
+        */
+#ifdef WIN32
+       _setmode(fileno(stderr), _O_BINARY);
+#endif
+
+       /* We don't want the postmaster's proc_exit() handlers */
+       on_exit_reset();
+
+       /* Initialize process-local latch support */
+       InitializeLatchSupport();
+       MyLatch = &LocalLatchData;
+       InitLatch(MyLatch);
+
+       /*
+        * If possible, make this process a group leader, so that the postmaster
+        * can signal any child processes too. Not all processes will have
+        * children, but for consistency we make all postmaster child processes do
+        * this.
+        */
+#ifdef HAVE_SETSID
+       if (setsid() < 0)
+               elog(FATAL, "setsid() failed: %m");
+#endif
+}
+
+/*
+ * Initialize the basic environment for a standalone process.
+ *
+ * argv0 has to be suitable to find the program's executable.
+ */
+void
+InitStandaloneProcess(const char *argv0)
+{
+       Assert(!IsPostmasterEnvironment);
+
+       MyProcPid = getpid();           /* reset MyProcPid */
+
+       MyStartTime = time(NULL);       /* set our start time in case we call elog */
+
+       /* Initialize process-local latch support */
+       InitializeLatchSupport();
+       MyLatch = &LocalLatchData;
+       InitLatch(MyLatch);
+
+       /* Compute paths, no postmaster to inherit from */
+       if (my_exec_path[0] == '\0')
+       {
+               if (find_my_exec(argv0, my_exec_path) < 0)
+                       elog(FATAL, "%s: could not locate my own executable path",
+                                argv0);
+       }
+
+       if (pkglib_path[0] == '\0')
+               get_pkglib_path(my_exec_path, pkglib_path);
+}
+
+void
+SwitchToSharedLatch(void)
+{
+       Assert(MyLatch == &LocalLatchData);
+       Assert(MyProc != NULL);
+
+       MyLatch = &MyProc->procLatch;
+
+       /*
+        * Set the shared latch as the local one might have been set. This
+        * shouldn't normally be necessary as code is supposed to check the
+        * condition before waiting for the latch, but a bit care can't hurt.
+        */
+       SetLatch(MyLatch);
+}
+
+void
+SwitchBackToLocalLatch(void)
+{
+       Assert(MyLatch != &LocalLatchData);
+       Assert(MyProc != NULL && MyLatch == &MyProc->procLatch);
+
+       MyLatch = &LocalLatchData;
+       SetLatch(MyLatch);
+}
 
 /*
  * GetUserId - get the current effective user ID.
@@ -293,15 +326,25 @@ SetSessionUserId(Oid userid, bool is_superuser)
        CurrentUserId = userid;
 }
 
+/*
+ * GetAuthenticatedUserId - get the authenticated user ID
+ */
+Oid
+GetAuthenticatedUserId(void)
+{
+       AssertState(OidIsValid(AuthenticatedUserId));
+       return AuthenticatedUserId;
+}
+
 
 /*
  * GetUserIdAndSecContext/SetUserIdAndSecContext - get/set the current user ID
  * and the SecurityRestrictionContext flags.
  *
- * Currently there are two valid bits in SecurityRestrictionContext:
+ * Currently there are three valid bits in SecurityRestrictionContext:
  *
  * SECURITY_LOCAL_USERID_CHANGE indicates that we are inside an operation
- * that is temporarily changing CurrentUserId via these functions.     This is
+ * that is temporarily changing CurrentUserId via these functions.  This is
  * needed to indicate that the actual value of CurrentUserId is not in sync
  * with guc.c's internal state, so SET ROLE has to be disallowed.
  *
@@ -316,13 +359,20 @@ SetSessionUserId(Oid userid, bool is_superuser)
  * where the called functions are really supposed to be side-effect-free
  * anyway, such as VACUUM/ANALYZE/REINDEX.
  *
+ * SECURITY_NOFORCE_RLS indicates that we are inside an operation which should
+ * ignore the FORCE ROW LEVEL SECURITY per-table indication.  This is used to
+ * ensure that FORCE RLS does not mistakenly break referential integrity
+ * checks.  Note that this is intentionally only checked when running as the
+ * owner of the table (which should always be the case for referential
+ * integrity checks).
+ *
  * Unlike GetUserId, GetUserIdAndSecContext does *not* Assert that the current
  * value of CurrentUserId is valid; nor does SetUserIdAndSecContext require
  * the new value to be valid.  In fact, these routines had better not
  * ever throw any kind of error.  This is because they are used by
  * StartTransaction and AbortTransaction to save/restore the settings,
  * and during the first transaction within a backend, the value to be saved
- * and perhaps restored is indeed invalid.     We have to be able to get
+ * and perhaps restored is indeed invalid.  We have to be able to get
  * through AbortTransaction without asserting in case InitPostgres fails.
  */
 void
@@ -358,11 +408,20 @@ InSecurityRestrictedOperation(void)
        return (SecurityRestrictionContext & SECURITY_RESTRICTED_OPERATION) != 0;
 }
 
+/*
+ * InNoForceRLSOperation - are we ignoring FORCE ROW LEVEL SECURITY ?
+ */
+bool
+InNoForceRLSOperation(void)
+{
+       return (SecurityRestrictionContext & SECURITY_NOFORCE_RLS) != 0;
+}
+
 
 /*
  * These are obsolete versions of Get/SetUserIdAndSecContext that are
  * only provided for bug-compatibility with some rather dubious code in
- * pljava.     We allow the userid to be set, but only when not inside a
+ * pljava.  We allow the userid to be set, but only when not inside a
  * security restriction context.
  */
 void
@@ -390,15 +449,15 @@ SetUserIdAndContext(Oid userid, bool sec_def_context)
 
 
 /*
- * Check if the authenticated user is a replication role
+ * Check whether specified role has explicit REPLICATION privilege
  */
 bool
-is_authenticated_user_replication_role(void)
+has_rolreplication(Oid roleid)
 {
        bool            result = false;
        HeapTuple       utup;
 
-       utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(AuthenticatedUserId));
+       utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
        if (HeapTupleIsValid(utup))
        {
                result = ((Form_pg_authid) GETSTRUCT(utup))->rolreplication;
@@ -411,11 +470,10 @@ is_authenticated_user_replication_role(void)
  * Initialize user identity during normal backend startup
  */
 void
-InitializeSessionUserId(const char *rolename)
+InitializeSessionUserId(const char *rolename, Oid roleid)
 {
        HeapTuple       roleTup;
        Form_pg_authid rform;
-       Oid                     roleid;
 
        /*
         * Don't do scans if we're bootstrapping, none of the system catalogs
@@ -426,7 +484,10 @@ InitializeSessionUserId(const char *rolename)
        /* call only once */
        AssertState(!OidIsValid(AuthenticatedUserId));
 
-       roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(rolename));
+       if (rolename != NULL)
+               roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(rolename));
+       else
+               roleTup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
        if (!HeapTupleIsValid(roleTup))
                ereport(FATAL,
                                (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -465,7 +526,7 @@ InitializeSessionUserId(const char *rolename)
                 * Check connection limit for this role.
                 *
                 * There is a race condition here --- we create our PGPROC before
-                * checking for other PGPROCs.  If two backends did this at about the
+                * checking for other PGPROCs.  If two backends did this at about the
                 * same time, they might both think they were over the limit, while
                 * ideally one should succeed and one fail.  Getting that to work
                 * exactly seems more trouble than it is worth, however; instead we
@@ -498,8 +559,8 @@ void
 InitializeSessionUserIdStandalone(void)
 {
        /*
-        * This function should only be called in single-user mode, in
-        * autovacuum workers, and in background workers.
+        * This function should only be called in single-user mode, in autovacuum
+        * workers, and in background workers.
         */
        AssertState(!IsUnderPostmaster || IsAutoVacuumWorkerProcess() || IsBackgroundWorker);
 
@@ -564,7 +625,7 @@ GetCurrentRoleId(void)
  * Change Role ID while running (SET ROLE)
  *
  * If roleid is InvalidOid, we are doing SET ROLE NONE: revert to the
- * session user authorization. In this case the is_superuser argument
+ * session user authorization.  In this case the is_superuser argument
  * is ignored.
  *
  * When roleid is not InvalidOid, the caller must have checked whether
@@ -604,23 +665,29 @@ SetCurrentRoleId(Oid roleid, bool is_superuser)
 
 
 /*
- * Get user name from user oid
+ * Get user name from user oid, returns NULL for nonexistent roleid if noerr
+ * is true.
  */
 char *
-GetUserNameFromId(Oid roleid)
+GetUserNameFromId(Oid roleid, bool noerr)
 {
        HeapTuple       tuple;
        char       *result;
 
        tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
        if (!HeapTupleIsValid(tuple))
-               ereport(ERROR,
-                               (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                errmsg("invalid role OID: %u", roleid)));
-
-       result = pstrdup(NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname));
-
-       ReleaseSysCache(tuple);
+       {
+               if (!noerr)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                        errmsg("invalid role OID: %u", roleid)));
+               result = NULL;
+       }
+       else
+       {
+               result = pstrdup(NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname));
+               ReleaseSysCache(tuple);
+       }
        return result;
 }
 
@@ -632,7 +699,7 @@ GetUserNameFromId(Oid roleid)
  * ($DATADIR/postmaster.pid) and Unix-socket-file lockfiles ($SOCKFILE.lock).
  * Both kinds of files contain the same info initially, although we can add
  * more information to a data-directory lockfile after it's created, using
- * AddToDataDirLockFile().     See miscadmin.h for documentation of the contents
+ * AddToDataDirLockFile().  See miscadmin.h for documentation of the contents
  * of these lockfiles.
  *
  * On successful lockfile creation, a proc_exit callback to remove the
@@ -721,7 +788,7 @@ CreateLockFile(const char *filename, bool amPostmaster,
                my_gp_pid = 0;
 
        /*
-        * We need a loop here because of race conditions.      But don't loop forever
+        * We need a loop here because of race conditions.  But don't loop forever
         * (for example, a non-writable $PGDATA directory might cause a failure
         * that won't go away).  100 tries seems like plenty.
         */
@@ -730,7 +797,7 @@ CreateLockFile(const char *filename, bool amPostmaster,
                /*
                 * Try to create the lock file --- O_EXCL makes this atomic.
                 *
-                * Think not to make the file protection weaker than 0600.      See
+                * Think not to make the file protection weaker than 0600.  See
                 * comments below.
                 */
                fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
@@ -798,7 +865,7 @@ CreateLockFile(const char *filename, bool amPostmaster,
                 * implies that the existing process has a different userid than we
                 * do, which means it cannot be a competing postmaster.  A postmaster
                 * cannot successfully attach to a data directory owned by a userid
-                * other than its own.  (This is now checked directly in
+                * other than its own.  (This is now checked directly in
                 * checkDataDir(), but has been true for a long time because of the
                 * restriction that the data directory isn't group- or
                 * world-accessible.)  Also, since we create the lockfiles mode 600,
@@ -836,9 +903,9 @@ CreateLockFile(const char *filename, bool amPostmaster,
                }
 
                /*
-                * No, the creating process did not exist.      However, it could be that
+                * No, the creating process did not exist.  However, it could be that
                 * the postmaster crashed (or more likely was kill -9'd by a clueless
-                * admin) but has left orphan backends behind.  Check for this by
+                * admin) but has left orphan backends behind.  Check for this by
                 * looking to see if there is an associated shmem segment that is
                 * still in use.
                 *
@@ -879,7 +946,7 @@ CreateLockFile(const char *filename, bool amPostmaster,
 
                /*
                 * Looks like nobody's home.  Unlink the file and try again to create
-                * it.  Need a loop because of possible race condition against other
+                * it.  Need a loop because of possible race condition against other
                 * would-be creators.
                 */
                if (unlink(filename) < 0)
@@ -893,7 +960,7 @@ CreateLockFile(const char *filename, bool amPostmaster,
        }
 
        /*
-        * Successfully created the file, now fill it.  See comment in miscadmin.h
+        * Successfully created the file, now fill it.  See comment in miscadmin.h
         * about the contents.  Note that we write the same first five lines into
         * both datadir and socket lockfiles; although more stuff may get added to
         * the datadir lockfile later.
@@ -948,14 +1015,18 @@ CreateLockFile(const char *filename, bool amPostmaster,
        }
 
        /*
-        * Arrange to unlink the lock file(s) at proc_exit.  If this is the
-        * first one, set up the on_proc_exit function to do it; then add this
-        * lock file to the list of files to unlink.
+        * Arrange to unlink the lock file(s) at proc_exit.  If this is the first
+        * one, set up the on_proc_exit function to do it; then add this lock file
+        * to the list of files to unlink.
         */
        if (lock_files == NIL)
                on_proc_exit(UnlinkLockFiles, 0);
 
-       lock_files = lappend(lock_files, pstrdup(filename));
+       /*
+        * Use lcons so that the lock files are unlinked in reverse order of
+        * creation; this is critical!
+        */
+       lock_files = lcons(pstrdup(filename), lock_files);
 }
 
 /*
@@ -1077,8 +1148,8 @@ AddToDataDirLockFile(int target_line, const char *str)
        srcbuffer[len] = '\0';
 
        /*
-        * Advance over lines we are not supposed to rewrite, then copy them
-        * to destbuffer.
+        * Advance over lines we are not supposed to rewrite, then copy them to
+        * destbuffer.
         */
        srcptr = srcbuffer;
        for (lineno = 1; lineno < target_line; lineno++)
@@ -1147,6 +1218,76 @@ AddToDataDirLockFile(int target_line, const char *str)
 }
 
 
+/*
+ * Recheck that the data directory lock file still exists with expected
+ * content.  Return TRUE if the lock file appears OK, FALSE if it isn't.
+ *
+ * We call this periodically in the postmaster.  The idea is that if the
+ * lock file has been removed or replaced by another postmaster, we should
+ * do a panic database shutdown.  Therefore, we should return TRUE if there
+ * is any doubt: we do not want to cause a panic shutdown unnecessarily.
+ * Transient failures like EINTR or ENFILE should not cause us to fail.
+ * (If there really is something wrong, we'll detect it on a future recheck.)
+ */
+bool
+RecheckDataDirLockFile(void)
+{
+       int                     fd;
+       int                     len;
+       long            file_pid;
+       char            buffer[BLCKSZ];
+
+       fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
+       if (fd < 0)
+       {
+               /*
+                * There are many foreseeable false-positive error conditions.  For
+                * safety, fail only on enumerated clearly-something-is-wrong
+                * conditions.
+                */
+               switch (errno)
+               {
+                       case ENOENT:
+                       case ENOTDIR:
+                               /* disaster */
+                               ereport(LOG,
+                                               (errcode_for_file_access(),
+                                                errmsg("could not open file \"%s\": %m",
+                                                               DIRECTORY_LOCK_FILE)));
+                               return false;
+                       default:
+                               /* non-fatal, at least for now */
+                               ereport(LOG,
+                                               (errcode_for_file_access(),
+                                 errmsg("could not open file \"%s\": %m; continuing anyway",
+                                                DIRECTORY_LOCK_FILE)));
+                               return true;
+               }
+       }
+       len = read(fd, buffer, sizeof(buffer) - 1);
+       if (len < 0)
+       {
+               ereport(LOG,
+                               (errcode_for_file_access(),
+                                errmsg("could not read from file \"%s\": %m",
+                                               DIRECTORY_LOCK_FILE)));
+               close(fd);
+               return true;                    /* treat read failure as nonfatal */
+       }
+       buffer[len] = '\0';
+       close(fd);
+       file_pid = atol(buffer);
+       if (file_pid == getpid())
+               return true;                    /* all is well */
+
+       /* Trouble: someone's overwritten the lock file */
+       ereport(LOG,
+                       (errmsg("lock file \"%s\" contains wrong PID: %ld instead of %ld",
+                                       DIRECTORY_LOCK_FILE, file_pid, (long) getpid())));
+       return false;
+}
+
+
 /*-------------------------------------------------------------------------
  *                             Version checking support
  *-------------------------------------------------------------------------
@@ -1222,6 +1363,7 @@ ValidatePgVersion(const char *path)
  * GUC variables: lists of library names to be preloaded at postmaster
  * start and at backend start
  */
+char      *session_preload_libraries_string = NULL;
 char      *shared_preload_libraries_string = NULL;
 char      *local_preload_libraries_string = NULL;
 
@@ -1239,7 +1381,6 @@ load_libraries(const char *libraries, const char *gucname, bool restricted)
 {
        char       *rawstring;
        List       *elemlist;
-       int                     elevel;
        ListCell   *l;
 
        if (libraries == NULL || libraries[0] == '\0')
@@ -1261,18 +1402,6 @@ load_libraries(const char *libraries, const char *gucname, bool restricted)
                return;
        }
 
-       /*
-        * Choose notice level: avoid repeat messages when re-loading a library
-        * that was preloaded into the postmaster.      (Only possible in EXEC_BACKEND
-        * configurations)
-        */
-#ifdef EXEC_BACKEND
-       if (IsUnderPostmaster && process_shared_preload_libraries_in_progress)
-               elevel = DEBUG2;
-       else
-#endif
-               elevel = LOG;
-
        foreach(l, elemlist)
        {
                char       *tok = (char *) lfirst(l);
@@ -1285,14 +1414,12 @@ load_libraries(const char *libraries, const char *gucname, bool restricted)
                {
                        char       *expanded;
 
-                       expanded = palloc(strlen("$libdir/plugins/") + strlen(filename) + 1);
-                       strcpy(expanded, "$libdir/plugins/");
-                       strcat(expanded, filename);
+                       expanded = psprintf("$libdir/plugins/%s", filename);
                        pfree(filename);
                        filename = expanded;
                }
                load_file(filename, restricted);
-               ereport(elevel,
+               ereport(DEBUG1,
                                (errmsg("loaded library \"%s\"", filename)));
                pfree(filename);
        }
@@ -1318,8 +1445,11 @@ process_shared_preload_libraries(void)
  * process any libraries that should be preloaded at backend start
  */
 void
-process_local_preload_libraries(void)
+process_session_preload_libraries(void)
 {
+       load_libraries(session_preload_libraries_string,
+                                  "session_preload_libraries",
+                                  false);
        load_libraries(local_preload_libraries_string,
                                   "local_preload_libraries",
                                   true);