posix_shmem.patch
application/octet-stream
Filename: posix_shmem.patch
Type: application/octet-stream
Part: 0
Message:
Re: POSIX shared memory redux
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| b/configure | 4 | 0 |
| b/configure.in | 2 | 0 |
| src/backend/bootstrap/bootstrap.c | 4 | 0 |
| src/backend/port/posix_shmem.c | 469 | 0 |
| src/backend/port/sysv_shmem.c | 0 | 550 |
| src/backend/postmaster/autovacuum.c | 3 | 0 |
| src/backend/postmaster/pgstat.c | 3 | 0 |
| src/backend/postmaster/postmaster.c | 6 | 0 |
| src/backend/tcop/postgres.c | 11 | 0 |
| src/backend/utils/init/miscinit.c | 344 | 3 |
| src/bin/pg_ctl/pg_ctl.c | 41 | 0 |
| src/include/miscadmin.h | 3 | 0 |
*** a/configure
--- b/configure
***************
*** 850,855 **** with_gnu_ld
--- 850,856 ----
enable_largefile
enable_float4_byval
enable_float8_byval
+ enable_float8_byval
'
ac_precious_vars='build_alias
host_alias
***************
*** 860,865 **** LDFLAGS
--- 861,867 ----
LIBS
CPPFLAGS
CPP
+ CPPFLAGS
LDFLAGS_EX
LDFLAGS_SL
DOCBOOKSTYLE'
***************
*** 28245,28254 **** fi
if test "$PORTNAME" != "win32"; then
cat >>confdefs.h <<\_ACEOF
! #define USE_SYSV_SHARED_MEMORY 1
_ACEOF
! SHMEM_IMPLEMENTATION="src/backend/port/sysv_shmem.c"
else
cat >>confdefs.h <<\_ACEOF
--- 28247,28256 ----
if test "$PORTNAME" != "win32"; then
cat >>confdefs.h <<\_ACEOF
! #define USE_POSIX_SHARED_MEMORY 1
_ACEOF
! SHMEM_IMPLEMENTATION="src/backend/port/posix_shmem.c"
else
cat >>confdefs.h <<\_ACEOF
*** a/configure.in
--- b/configure.in
***************
*** 1730,1737 **** fi
# Select shared-memory implementation type.
if test "$PORTNAME" != "win32"; then
! AC_DEFINE(USE_SYSV_SHARED_MEMORY, 1, [Define to select SysV-style shared memory.])
! SHMEM_IMPLEMENTATION="src/backend/port/sysv_shmem.c"
else
AC_DEFINE(USE_WIN32_SHARED_MEMORY, 1, [Define to select Win32-style shared memory.])
SHMEM_IMPLEMENTATION="src/backend/port/win32_shmem.c"
--- 1730,1737 ----
# Select shared-memory implementation type.
if test "$PORTNAME" != "win32"; then
! AC_DEFINE(USE_POSIX_SHARED_MEMORY, 1, [Define to select SysV-style shared memory.])
! SHMEM_IMPLEMENTATION="src/backend/port/posix_shmem.c"
else
AC_DEFINE(USE_WIN32_SHARED_MEMORY, 1, [Define to select Win32-style shared memory.])
SHMEM_IMPLEMENTATION="src/backend/port/win32_shmem.c"
*** a/src/backend/bootstrap/bootstrap.c
--- b/src/backend/bootstrap/bootstrap.c
***************
*** 352,358 **** AuxiliaryProcessMain(int argc, char *argv[])
/* If standalone, create lockfile for data directory */
if (!IsUnderPostmaster)
! CreateDataDirLockFile(false);
SetProcessingMode(BootstrapProcessing);
IgnoreSystemIndexes = true;
--- 352,361 ----
/* If standalone, create lockfile for data directory */
if (!IsUnderPostmaster)
! CreateDataDirLockFile(false,false);
!
! /* Hold on to the lock file for the life of this process. */
! AcquireDataDirLock();
SetProcessingMode(BootstrapProcessing);
IgnoreSystemIndexes = true;
*** /dev/null
--- b/src/backend/port/posix_shmem.c
***************
*** 0 ****
--- 1,469 ----
+ /*-------------------------------------------------------------------------
+ *
+ * posix_shmem.c
+ * Implement shared memory using POSIX facilities
+ *
+ * These routines represent a fairly thin layer on top of POSIX shared
+ * memory functionality.
+ *
+ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include <signal.h>
+ #include <unistd.h>
+ #include <sys/file.h>
+ #include <sys/mman.h>
+ #include <sys/param.h>
+ #include <sys/stat.h>
+ #include <sys/types.h>
+ #ifdef HAVE_KERNEL_OS_H
+ #include <kernel/OS.h>
+ #endif
+
+ #include "miscadmin.h"
+ #include "libpq/md5.h"
+ #include "storage/ipc.h"
+ #include "storage/pg_shmem.h"
+
+
+ #define IPCProtection (0600) /* access/modify by user only */
+ #define IPCNameLength 31 /* Darwin requires max 30 + '\0' */
+
+ uint8 UsedShmemInstanceId = 0;
+ void *UsedShmemSegAddr = NULL;
+
+ static void GenerateIPCName(uint8 instanceId, char destIPCName[IPCNameLength]);
+ static void *InternalIpcMemoryCreate(const char ipcName[IPCNameLength],uint8 instanceId, Size size);
+ static void IpcMemoryDetach(int status, Datum shmaddr);
+ static int POSIXSharedMemoryFD=-1;
+
+
+ /*
+ * GenerateIPCName(instanceId, destIPCName)
+ *
+ * Generate a shared memory object key name using the implicit argument
+ * DataDir's pathname and the current instance id. A hash of the
+ * canonicalized directory path is used to construct the key name.
+ * Store the result in destIPCName, which must be IPCNameLength bytes.
+ */
+ static void
+ GenerateIPCName(uint8 instanceId, char destIPCName[IPCNameLength])
+ {
+
+ /* This must be 30 characters or less for portability (i.e. Darwin).
+ * POSIX requires shared memory names to begin with a single slash. It
+ * should not have any others slashes or any non-alphanumerics as the
+ * that is the broadest assumption of what is permitted in a filename.
+ * Also, case sensitivity should not be presumed.
+ *
+ * Collisions are averted by the fact that the shared memory region is
+ * immediately unlinked.
+ *
+ * The string is formed starting with a slash, then the identifier 'PG.',
+ * then the pid of the current process.
+ */
+ snprintf(destIPCName, IPCNameLength, "/PG.%6ld", (long int)getpid());
+ }
+
+ /*
+ * InternalIpcMemoryCreate(ipcName, size)
+ *
+ * Attempt to create a new shared memory segment with the specified IPC name.
+ * Will fail (return NULL) if such a segment already exists. If successful,
+ * attach the segment to the current process and return its attached address.
+ * On success, callbacks are registered with on_shmem_exit to detach and
+ * delete the segment when on_shmem_exit is called.
+ *
+ * If we fail with a failure code other than collision-with-existing-segment,
+ * print out an error and abort. Other types of errors are not recoverable.
+ */
+ static void *
+ InternalIpcMemoryCreate(const char ipcName[IPCNameLength], uint8 instanceId, Size size)
+ {
+ int fd;
+ int unlink_status=0;
+ int fstat_status=0;
+ int ftruncate_status=0;
+ void *shmaddr;
+ struct stat statbuf;
+
+ fd = shm_open(ipcName, O_RDWR | O_CREAT | O_EXCL, IPCProtection);
+
+ if (fd < 0)
+ {
+ /*
+ * Fail quietly if error indicates a collision with existing segment.
+ * One would expect EEXIST, given that we said O_EXCL.
+ */
+ if (errno == EEXIST || errno == EACCES || errno == EINTR)
+ return NULL;
+
+ /*
+ * Else complain and abort
+ */
+ ereport(FATAL,
+ (errmsg("could not create shared memory segment: %m"),
+ errdetail("Failed system call was shm_open(name=%s, oflag=%lu, mode=%lu).",
+ ipcName, (unsigned long) O_CREAT | O_EXCL,
+ (unsigned long) IPCProtection),
+ (errno == EMFILE) ?
+ errhint("This error means that the process has reached its limit "
+ "for open file descriptors.") : 0,
+ (errno == ENOSPC) ?
+ errhint("This error means the process has ran out of address "
+ "space.") : 0));
+ }
+ /* the race between creation and unlinking is protected by the shared memory pid file */
+
+
+ unlink_status = shm_unlink(ipcName);
+ if(unlink_status<0)
+ {
+ /* It would be virtually impossible for us to fail to unlink a shared memory region we just created, but we need to handle this anyway- refuse to use this shared memory segment. */
+ ereport(FATAL,
+ (errmsg("could not unlink shared memory segment : %m"),
+ errdetail("Failed system call was shm_unlink(name=%s).",ipcName)));
+ return NULL;
+ }
+
+ /* Increase the size of the file descriptor to the desired length.
+ * If this fails so will mmap since it can't map size bytes. */
+ fstat_status = fstat(fd, &statbuf);
+ if(fstat_status<0)
+ {
+ ereport(FATAL,
+ (errmsg("could not fstat the shared memory segment : %m"),
+ errdetail("Failed system call was fstat(fd=%d,stat=%p).",fd,&statbuf)));
+ return NULL;
+ }
+ if (statbuf.st_size < size)
+ {
+ ftruncate_status = ftruncate(fd, size);
+ if(ftruncate_status<0)
+ {
+ ereport(FATAL,
+ (errmsg("could not set the proper shared memory segment size : %m"),
+ errdetail("Failed system call was ftruncate(fd=%d,size=%lu).",fd,size)));
+ return NULL;
+ }
+ }
+
+ /* OK, should be able to attach to the segment */
+ shmaddr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (shmaddr == MAP_FAILED)
+ elog(FATAL, "mmap with size=%ul and fd=%d failed: %m", (unsigned int) size, fd);
+
+ /* Register on-exit routine to detach new segment before deleting */
+ on_shmem_exit(IpcMemoryDetach, PointerGetDatum(shmaddr));
+
+ POSIXSharedMemoryFD = fd;
+ return shmaddr;
+ }
+
+ /****************************************************************************/
+ /* IpcMemoryDetach(status, shmaddr) removes a shared memory segment */
+ /* from process' address space */
+ /* (called as an on_shmem_exit callback, hence funny argument list) */
+ /****************************************************************************/
+ static void
+ IpcMemoryDetach(int status, Datum shmaddr)
+ {
+ PGShmemHeader *hdr;
+ hdr = (PGShmemHeader *) DatumGetPointer(shmaddr);
+
+ if (munmap(DatumGetPointer(shmaddr), hdr->totalsize) < 0)
+ elog(LOG, "munmap(%p, ...) failed: %m", DatumGetPointer(shmaddr));
+ }
+
+ /*
+ * PGSharedMemoryIsInUse
+ *
+ * Is a previously-existing shmem segment still existing and in use?
+ *
+ * The point of this exercise is to detect the case where a prior postmaster
+ * crashed, but it left child backends that are still running. Therefore
+ * we only care about shmem segments that are associated with the intended
+ * DataDir. This is an important consideration since accidental matches of
+ * shmem segment IDs are reasonably common.
+ */
+ bool
+ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
+ {
+ char ipcName[IPCNameLength];
+ PGShmemHeader *hdr;
+ int fd, isValidHeader;
+
+ #ifndef WIN32
+ struct stat statbuf;
+ #endif
+
+ /*
+ * We detect whether a shared memory segment is in use by seeing whether
+ * we can open it. If so,
+ */
+ GenerateIPCName((uint8) id1, ipcName);
+ fd = shm_open(ipcName, O_RDWR, 0);
+ if (fd < 0)
+ {
+ /*
+ * ENOENT means the segment no longer exists.
+ */
+ if (errno == ENOENT)
+ return false;
+
+ /*
+ * EACCES implies that the segment belongs to some other userid, which
+ * means that there is an different account with the same database open.
+ */
+ if (errno == EACCES)
+ return true;
+ }
+
+ /*
+ * Try to attach to the segment and see if it matches our data directory,
+ * just as a sanity check. Note that this is not absolutely necessary
+ * since the data directory is encoded in the IPC shared memory key name.
+ *
+ * On Windows, which doesn't have useful inode numbers, we can't do this
+ * so we punt and assume that the shared memory is valid (which in all
+ * likelihood it is).
+ */
+ #ifdef WIN32
+ close(fd);
+ return true;
+ #else
+ if (stat(DataDir, &statbuf) < 0)
+ {
+ close(fd);
+ return true; /* if can't stat, be conservative */
+ }
+
+ hdr = (PGShmemHeader *) mmap(NULL, sizeof(PGShmemHeader), PROT_READ, MAP_SHARED, fd, 0);
+ close(fd);
+
+ if (hdr == (PGShmemHeader *) -1)
+ return true; /* if can't attach, be conservative */
+
+ isValidHeader = hdr->magic == PGShmemMagic &&
+ hdr->device == statbuf.st_dev &&
+ hdr->inode == statbuf.st_ino;
+ munmap((void *) hdr, sizeof(PGShmemHeader));
+
+ /*
+ * If true, it's either not a Postgres segment, or not one for my data
+ * directory. In either case it poses no threat.
+ * If false, trouble -- looks a lot like there are still live backends
+ */
+
+ return isValidHeader;
+ #endif
+ }
+
+
+ /*
+ * PGSharedMemoryCreate
+ *
+ * Create a shared memory segment of the given size and initialize its
+ * standard header. Also, register an on_shmem_exit callback to release
+ * the storage.
+ *
+ * Dead Postgres segments are released when found, but we do not fail upon
+ * collision with non-Postgres shmem segments, although this is astronomically
+ * unlikely.
+ *
+ * makePrivate means to always create a new segment, rather than attach to
+ * or recycle any existing segment. Currently, this value is ignored as
+ * all segments are newly created (the dead ones are simply released).
+ *
+ * Port is ignored. (It is leftover from the SysV shared memory routines.)
+ */
+ PGShmemHeader *
+ PGSharedMemoryCreate(Size size, bool makePrivate, int port)
+ {
+ uint8 instanceId;
+ void *shmaddr;
+ PGShmemHeader *hdr;
+ char ipcName[IPCNameLength];
+
+ #ifndef WIN32
+ struct stat statbuf;
+ #endif
+
+ /* Room for a header? */
+ Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+
+ /* Make sure PGSharedMemoryAttach doesn't fail without need */
+ UsedShmemSegAddr = NULL;
+
+ /* Loop till we find a free IPC key */
+ for (instanceId = 0; true; instanceId++)
+ {
+ /*
+ * Try to create new segment. InternalIpcMemoryCreate encodes the data
+ * directory path name into the IPC key name, so if this fails
+ * one of three things has happened:
+ * 1) there is another postmaster still running with the same data directory
+ * 2) the postmaster in this directory crashed or was kill -9'd
+ * and there are backends still running.
+ * 3) the postmaster in this directory crashed or was kill -9'd and there
+ * are no backends still running, just an orpaned shmem segment
+ *
+ * Case 1 is handled by the postmaster.pid file and doesn't concern us here.
+ * For case 2 & 3 we now should unlink the shmem segment so that it is
+ * cleaned up, either now (case 3) or when the backends terminate (case 2).
+ * Then we should try the next instanceId to create a new segment so this
+ * process can be up and running quickly.
+ */
+ GenerateIPCName(instanceId, ipcName);
+ shmaddr = InternalIpcMemoryCreate(ipcName, instanceId, size);
+ if (shmaddr)
+ break; /* successful create and attach */
+
+ /*
+ * The segment appears to be from a dead Postgres process, or from a
+ * previous cycle of life in this same process. Zap it, if possible.
+ * This shouldn't fail, but if it does, assume the segment
+ * belongs to someone else after all, and continue quietly.
+ */
+ shm_unlink(ipcName);
+ }
+
+ /* OK, we created a new segment. Mark it as created by this process. */
+ hdr = (PGShmemHeader *) shmaddr;
+ hdr->creatorPID = getpid();
+ hdr->magic = PGShmemMagic;
+
+ #ifndef WIN32
+ /* Fill in the data directory ID info, too */
+ if (stat(DataDir, &statbuf) < 0)
+ ereport(FATAL,
+ (errcode_for_file_access(),
+ errmsg("could not stat data directory \"%s\": %m",
+ DataDir)));
+ hdr->device = statbuf.st_dev;
+ hdr->inode = statbuf.st_ino;
+ #endif
+
+ /* Initialize space allocation status for segment. */
+ hdr->totalsize = size;
+ hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
+
+ /* Save info for possible future use */
+ UsedShmemInstanceId = instanceId;
+ UsedShmemSegAddr = shmaddr;
+
+ return hdr;
+ }
+
+ #ifdef EXEC_BACKEND
+
+ /*
+ * PGSharedMemoryReAttach
+ *
+ * Re-attach to an already existing shared memory segment. In the non
+ * EXEC_BACKEND case this is not used, because postmaster children inherit
+ * the shared memory segment attachment via fork().
+ *
+ * UsedShmemInstanceId and UsedShmemSegAddr are implicit parameters to this
+ * routine. The caller must have already restored them to the postmaster's
+ * values.
+ */
+ void
+ PGSharedMemoryReAttach(void)
+ {
+ int fd;
+ void *hdr;
+ void *origUsedShmemSegAddr = UsedShmemSegAddr;
+
+ Assert(UsedShmemSegAddr != NULL);
+ Assert(IsUnderPostmaster);
+
+ #ifdef __CYGWIN__
+ /* cygipc (currently) appears to not detach on exec. */
+ PGSharedMemoryDetach();
+ UsedShmemSegAddr = origUsedShmemSegAddr;
+ #endif
+
+ elog(DEBUG3, "attaching to %p", UsedShmemSegAddr);
+ hdr = (void *) PGSharedMemoryAttach(UsedShmemInstanceId);
+ if (hdr == NULL)
+ elog(FATAL, "could not reattach to shared memory (instanceId=%d, addr=%p): %m",
+ (int) UsedShmemInstanceId, UsedShmemSegAddr);
+ if (hdr != origUsedShmemSegAddr)
+ elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
+ hdr, origUsedShmemSegAddr);
+
+ UsedShmemSegAddr = hdr; /* probably redundant */
+ }
+
+
+ /*
+ * Attach to shared memory and make sure it has a Postgres header
+ *
+ * Returns attach address if OK, else NULL
+ */
+ static PGShmemHeader *
+ PGSharedMemoryAttach(uint8 instanceId)
+ {
+ PGShmemHeader *hdr;
+ char ipcName[IPCNameLength];
+ Size size;
+ int fd;
+
+ fd = POSIXSharedMemoryFD;
+
+ if (fd < 0)
+ return NULL;
+
+ hdr = (PGShmemHeader *) mmap(UsedShmemSegAddr, sizeof(PGShmemHeader),
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (hdr == MMAP_FAILED)
+ {
+ return NULL; /* failed to mmap- unlikely */
+ }
+
+ if (hdr->magic != PGShmemMagic)
+ {
+ munmap((void *) hdr, sizeof(PGShmemHeader));
+ return NULL; /* segment belongs to a non-Postgres app */
+ }
+
+ /* Since the segment has a valid Postgres header, unmap and re-map it with the proper size */
+ size = hdr->totalsize;
+ munmap((void *) hdr, sizeof(PGShmemHeader));
+ hdr = (PGShmemHeader *) mmap(UsedShmemSegAddr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (hdr == MMAP_FAILED) /* this shouldn't happen */
+ return NULL;
+
+ return hdr;
+ }
+ #endif /* EXEC_BACKEND */
+
+ /*
+ * PGSharedMemoryDetach
+ *
+ * Detach from the shared memory segment, if still attached. This is not
+ * intended for use by the process that originally created the segment
+ * (it will have an on_shmem_exit callback registered to do that). Rather,
+ * this is for subprocesses that have inherited an attachment and want to
+ * get rid of it.
+ */
+ void
+ PGSharedMemoryDetach(void)
+ {
+ PGShmemHeader *hdr;
+ if (UsedShmemSegAddr != NULL)
+ {
+ hdr = (PGShmemHeader *) UsedShmemSegAddr;
+ if (munmap(UsedShmemSegAddr, hdr->totalsize) < 0)
+ elog(LOG, "munmap(%p) failed: %m", UsedShmemSegAddr);
+ UsedShmemSegAddr = NULL;
+ }
+ }
*** a/src/backend/port/sysv_shmem.c
--- /dev/null
***************
*** 1,550 ****
- /*-------------------------------------------------------------------------
- *
- * sysv_shmem.c
- * Implement shared memory using SysV facilities
- *
- * These routines represent a fairly thin layer on top of SysV shared
- * memory functionality.
- *
- * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * src/backend/port/sysv_shmem.c
- *
- *-------------------------------------------------------------------------
- */
- #include "postgres.h"
-
- #include <signal.h>
- #include <unistd.h>
- #include <sys/file.h>
- #include <sys/stat.h>
- #ifdef HAVE_SYS_IPC_H
- #include <sys/ipc.h>
- #endif
- #ifdef HAVE_SYS_SHM_H
- #include <sys/shm.h>
- #endif
- #ifdef HAVE_KERNEL_OS_H
- #include <kernel/OS.h>
- #endif
-
- #include "miscadmin.h"
- #include "storage/ipc.h"
- #include "storage/pg_shmem.h"
-
-
- typedef key_t IpcMemoryKey; /* shared memory key passed to shmget(2) */
- typedef int IpcMemoryId; /* shared memory ID returned by shmget(2) */
-
- #define IPCProtection (0600) /* access/modify by user only */
-
- #ifdef SHM_SHARE_MMU /* use intimate shared memory on Solaris */
- #define PG_SHMAT_FLAGS SHM_SHARE_MMU
- #else
- #define PG_SHMAT_FLAGS 0
- #endif
-
-
- unsigned long UsedShmemSegID = 0;
- void *UsedShmemSegAddr = NULL;
-
- static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
- static void IpcMemoryDetach(int status, Datum shmaddr);
- static void IpcMemoryDelete(int status, Datum shmId);
- static PGShmemHeader *PGSharedMemoryAttach(IpcMemoryKey key,
- IpcMemoryId *shmid);
-
-
- /*
- * InternalIpcMemoryCreate(memKey, size)
- *
- * Attempt to create a new shared memory segment with the specified key.
- * Will fail (return NULL) if such a segment already exists. If successful,
- * attach the segment to the current process and return its attached address.
- * On success, callbacks are registered with on_shmem_exit to detach and
- * delete the segment when on_shmem_exit is called.
- *
- * If we fail with a failure code other than collision-with-existing-segment,
- * print out an error and abort. Other types of errors are not recoverable.
- */
- static void *
- InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
- {
- IpcMemoryId shmid;
- void *memAddress;
-
- shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection);
-
- if (shmid < 0)
- {
- /*
- * Fail quietly if error indicates a collision with existing segment.
- * One would expect EEXIST, given that we said IPC_EXCL, but perhaps
- * we could get a permission violation instead? Also, EIDRM might
- * occur if an old seg is slated for destruction but not gone yet.
- */
- if (errno == EEXIST || errno == EACCES
- #ifdef EIDRM
- || errno == EIDRM
- #endif
- )
- return NULL;
-
- /*
- * Some BSD-derived kernels are known to return EINVAL, not EEXIST, if
- * there is an existing segment but it's smaller than "size" (this is
- * a result of poorly-thought-out ordering of error tests). To
- * distinguish between collision and invalid size in such cases, we
- * make a second try with size = 0. These kernels do not test size
- * against SHMMIN in the preexisting-segment case, so we will not get
- * EINVAL a second time if there is such a segment.
- */
- if (errno == EINVAL)
- {
- int save_errno = errno;
-
- shmid = shmget(memKey, 0, IPC_CREAT | IPC_EXCL | IPCProtection);
-
- if (shmid < 0)
- {
- /* As above, fail quietly if we verify a collision */
- if (errno == EEXIST || errno == EACCES
- #ifdef EIDRM
- || errno == EIDRM
- #endif
- )
- return NULL;
- /* Otherwise, fall through to report the original error */
- }
- else
- {
- /*
- * On most platforms we cannot get here because SHMMIN is
- * greater than zero. However, if we do succeed in creating a
- * zero-size segment, free it and then fall through to report
- * the original error.
- */
- if (shmctl(shmid, IPC_RMID, NULL) < 0)
- elog(LOG, "shmctl(%d, %d, 0) failed: %m",
- (int) shmid, IPC_RMID);
- }
-
- errno = save_errno;
- }
-
- /*
- * Else complain and abort.
- *
- * Note: at this point EINVAL should mean that either SHMMIN or SHMMAX
- * is violated. SHMALL violation might be reported as either ENOMEM
- * (BSDen) or ENOSPC (Linux); the Single Unix Spec fails to say which
- * it should be. SHMMNI violation is ENOSPC, per spec. Just plain
- * not-enough-RAM is ENOMEM.
- */
- ereport(FATAL,
- (errmsg("could not create shared memory segment: %m"),
- errdetail("Failed system call was shmget(key=%lu, size=%lu, 0%o).",
- (unsigned long) memKey, (unsigned long) size,
- IPC_CREAT | IPC_EXCL | IPCProtection),
- (errno == EINVAL) ?
- errhint("This error usually means that PostgreSQL's request for a shared memory "
- "segment exceeded your kernel's SHMMAX parameter. You can either "
- "reduce the request size or reconfigure the kernel with larger SHMMAX. "
- "To reduce the request size (currently %lu bytes), reduce "
- "PostgreSQL's shared memory usage, perhaps by reducing shared_buffers "
- "or max_connections.\n"
- "If the request size is already small, it's possible that it is less than "
- "your kernel's SHMMIN parameter, in which case raising the request size or "
- "reconfiguring SHMMIN is called for.\n"
- "The PostgreSQL documentation contains more information about shared "
- "memory configuration.",
- (unsigned long) size) : 0,
- (errno == ENOMEM) ?
- errhint("This error usually means that PostgreSQL's request for a shared "
- "memory segment exceeded available memory or swap space, "
- "or exceeded your kernel's SHMALL parameter. You can either "
- "reduce the request size or reconfigure the kernel with larger SHMALL. "
- "To reduce the request size (currently %lu bytes), reduce "
- "PostgreSQL's shared memory usage, perhaps by reducing shared_buffers "
- "or max_connections.\n"
- "The PostgreSQL documentation contains more information about shared "
- "memory configuration.",
- (unsigned long) size) : 0,
- (errno == ENOSPC) ?
- errhint("This error does *not* mean that you have run out of disk space. "
- "It occurs either if all available shared memory IDs have been taken, "
- "in which case you need to raise the SHMMNI parameter in your kernel, "
- "or because the system's overall limit for shared memory has been "
- "reached. If you cannot increase the shared memory limit, "
- "reduce PostgreSQL's shared memory request (currently %lu bytes), "
- "perhaps by reducing shared_buffers or max_connections.\n"
- "The PostgreSQL documentation contains more information about shared "
- "memory configuration.",
- (unsigned long) size) : 0));
- }
-
- /* Register on-exit routine to delete the new segment */
- on_shmem_exit(IpcMemoryDelete, Int32GetDatum(shmid));
-
- /* OK, should be able to attach to the segment */
- memAddress = shmat(shmid, NULL, PG_SHMAT_FLAGS);
-
- if (memAddress == (void *) -1)
- elog(FATAL, "shmat(id=%d) failed: %m", shmid);
-
- /* Register on-exit routine to detach new segment before deleting */
- on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress));
-
- /*
- * Store shmem key and ID in data directory lockfile. Format to try to
- * keep it the same length always (trailing junk in the lockfile won't
- * hurt, but might confuse humans).
- */
- {
- char line[64];
-
- sprintf(line, "%9lu %9lu",
- (unsigned long) memKey, (unsigned long) shmid);
- AddToDataDirLockFile(LOCK_FILE_LINE_SHMEM_KEY, line);
- }
-
- return memAddress;
- }
-
- /****************************************************************************/
- /* IpcMemoryDetach(status, shmaddr) removes a shared memory segment */
- /* from process' address spaceq */
- /* (called as an on_shmem_exit callback, hence funny argument list) */
- /****************************************************************************/
- static void
- IpcMemoryDetach(int status, Datum shmaddr)
- {
- if (shmdt(DatumGetPointer(shmaddr)) < 0)
- elog(LOG, "shmdt(%p) failed: %m", DatumGetPointer(shmaddr));
- }
-
- /****************************************************************************/
- /* IpcMemoryDelete(status, shmId) deletes a shared memory segment */
- /* (called as an on_shmem_exit callback, hence funny argument list) */
- /****************************************************************************/
- static void
- IpcMemoryDelete(int status, Datum shmId)
- {
- if (shmctl(DatumGetInt32(shmId), IPC_RMID, NULL) < 0)
- elog(LOG, "shmctl(%d, %d, 0) failed: %m",
- DatumGetInt32(shmId), IPC_RMID);
- }
-
- /*
- * PGSharedMemoryIsInUse
- *
- * Is a previously-existing shmem segment still existing and in use?
- *
- * The point of this exercise is to detect the case where a prior postmaster
- * crashed, but it left child backends that are still running. Therefore
- * we only care about shmem segments that are associated with the intended
- * DataDir. This is an important consideration since accidental matches of
- * shmem segment IDs are reasonably common.
- */
- bool
- PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
- {
- IpcMemoryId shmId = (IpcMemoryId) id2;
- struct shmid_ds shmStat;
- struct stat statbuf;
- PGShmemHeader *hdr;
-
- /*
- * We detect whether a shared memory segment is in use by seeing whether
- * it (a) exists and (b) has any processes attached to it.
- */
- if (shmctl(shmId, IPC_STAT, &shmStat) < 0)
- {
- /*
- * EINVAL actually has multiple possible causes documented in the
- * shmctl man page, but we assume it must mean the segment no longer
- * exists.
- */
- if (errno == EINVAL)
- return false;
-
- /*
- * EACCES implies that the segment belongs to some other userid, which
- * means it is not a Postgres shmem segment (or at least, not one that
- * is relevant to our data directory).
- */
- if (errno == EACCES)
- return false;
-
- /*
- * Some Linux kernel versions (in fact, all of them as of July 2007)
- * sometimes return EIDRM when EINVAL is correct. The Linux kernel
- * actually does not have any internal state that would justify
- * returning EIDRM, so we can get away with assuming that EIDRM is
- * equivalent to EINVAL on that platform.
- */
- #ifdef HAVE_LINUX_EIDRM_BUG
- if (errno == EIDRM)
- return false;
- #endif
-
- /*
- * Otherwise, we had better assume that the segment is in use. The
- * only likely case is EIDRM, which implies that the segment has been
- * IPC_RMID'd but there are still processes attached to it.
- */
- return true;
- }
-
- /* If it has no attached processes, it's not in use */
- if (shmStat.shm_nattch == 0)
- return false;
-
- /*
- * Try to attach to the segment and see if it matches our data directory.
- * This avoids shmid-conflict problems on machines that are running
- * several postmasters under the same userid.
- */
- if (stat(DataDir, &statbuf) < 0)
- return true; /* if can't stat, be conservative */
-
- hdr = (PGShmemHeader *) shmat(shmId, NULL, PG_SHMAT_FLAGS);
-
- if (hdr == (PGShmemHeader *) -1)
- return true; /* if can't attach, be conservative */
-
- if (hdr->magic != PGShmemMagic ||
- hdr->device != statbuf.st_dev ||
- hdr->inode != statbuf.st_ino)
- {
- /*
- * It's either not a Postgres segment, or not one for my data
- * directory. In either case it poses no threat.
- */
- shmdt((void *) hdr);
- return false;
- }
-
- /* Trouble --- looks a lot like there's still live backends */
- shmdt((void *) hdr);
-
- return true;
- }
-
-
- /*
- * PGSharedMemoryCreate
- *
- * Create a shared memory segment of the given size and initialize its
- * standard header. Also, register an on_shmem_exit callback to release
- * the storage.
- *
- * Dead Postgres segments are recycled if found, but we do not fail upon
- * collision with non-Postgres shmem segments. The idea here is to detect and
- * re-use keys that may have been assigned by a crashed postmaster or backend.
- *
- * makePrivate means to always create a new segment, rather than attach to
- * or recycle any existing segment.
- *
- * The port number is passed for possible use as a key (for SysV, we use
- * it to generate the starting shmem key). In a standalone backend,
- * zero will be passed.
- */
- PGShmemHeader *
- PGSharedMemoryCreate(Size size, bool makePrivate, int port)
- {
- IpcMemoryKey NextShmemSegID;
- void *memAddress;
- PGShmemHeader *hdr;
- IpcMemoryId shmid;
- struct stat statbuf;
-
- /* Room for a header? */
- Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
-
- /* Make sure PGSharedMemoryAttach doesn't fail without need */
- UsedShmemSegAddr = NULL;
-
- /* Loop till we find a free IPC key */
- NextShmemSegID = port * 1000;
-
- for (NextShmemSegID++;; NextShmemSegID++)
- {
- /* Try to create new segment */
- memAddress = InternalIpcMemoryCreate(NextShmemSegID, size);
- if (memAddress)
- break; /* successful create and attach */
-
- /* Check shared memory and possibly remove and recreate */
-
- if (makePrivate) /* a standalone backend shouldn't do this */
- continue;
-
- if ((memAddress = PGSharedMemoryAttach(NextShmemSegID, &shmid)) == NULL)
- continue; /* can't attach, not one of mine */
-
- /*
- * If I am not the creator and it belongs to an extant process,
- * continue.
- */
- hdr = (PGShmemHeader *) memAddress;
- if (hdr->creatorPID != getpid())
- {
- if (kill(hdr->creatorPID, 0) == 0 || errno != ESRCH)
- {
- shmdt(memAddress);
- continue; /* segment belongs to a live process */
- }
- }
-
- /*
- * The segment appears to be from a dead Postgres process, or from a
- * previous cycle of life in this same process. Zap it, if possible.
- * This probably shouldn't fail, but if it does, assume the segment
- * belongs to someone else after all, and continue quietly.
- */
- shmdt(memAddress);
- if (shmctl(shmid, IPC_RMID, NULL) < 0)
- continue;
-
- /*
- * Now try again to create the segment.
- */
- memAddress = InternalIpcMemoryCreate(NextShmemSegID, size);
- if (memAddress)
- break; /* successful create and attach */
-
- /*
- * Can only get here if some other process managed to create the same
- * shmem key before we did. Let him have that one, loop around to try
- * next key.
- */
- }
-
- /*
- * OK, we created a new segment. Mark it as created by this process. The
- * order of assignments here is critical so that another Postgres process
- * can't see the header as valid but belonging to an invalid PID!
- */
- hdr = (PGShmemHeader *) memAddress;
- hdr->creatorPID = getpid();
- hdr->magic = PGShmemMagic;
-
- /* Fill in the data directory ID info, too */
- if (stat(DataDir, &statbuf) < 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not stat data directory \"%s\": %m",
- DataDir)));
- hdr->device = statbuf.st_dev;
- hdr->inode = statbuf.st_ino;
-
- /*
- * Initialize space allocation status for segment.
- */
- hdr->totalsize = size;
- hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
-
- /* Save info for possible future use */
- UsedShmemSegAddr = memAddress;
- UsedShmemSegID = (unsigned long) NextShmemSegID;
-
- return hdr;
- }
-
- #ifdef EXEC_BACKEND
-
- /*
- * PGSharedMemoryReAttach
- *
- * Re-attach to an already existing shared memory segment. In the non
- * EXEC_BACKEND case this is not used, because postmaster children inherit
- * the shared memory segment attachment via fork().
- *
- * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
- * routine. The caller must have already restored them to the postmaster's
- * values.
- */
- void
- PGSharedMemoryReAttach(void)
- {
- IpcMemoryId shmid;
- void *hdr;
- void *origUsedShmemSegAddr = UsedShmemSegAddr;
-
- Assert(UsedShmemSegAddr != NULL);
- Assert(IsUnderPostmaster);
-
- #ifdef __CYGWIN__
- /* cygipc (currently) appears to not detach on exec. */
- PGSharedMemoryDetach();
- UsedShmemSegAddr = origUsedShmemSegAddr;
- #endif
-
- elog(DEBUG3, "attaching to %p", UsedShmemSegAddr);
- hdr = (void *) PGSharedMemoryAttach((IpcMemoryKey) UsedShmemSegID, &shmid);
- if (hdr == NULL)
- elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %m",
- (int) UsedShmemSegID, UsedShmemSegAddr);
- if (hdr != origUsedShmemSegAddr)
- elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
- hdr, origUsedShmemSegAddr);
-
- UsedShmemSegAddr = hdr; /* probably redundant */
- }
- #endif /* EXEC_BACKEND */
-
- /*
- * PGSharedMemoryDetach
- *
- * Detach from the shared memory segment, if still attached. This is not
- * intended for use by the process that originally created the segment
- * (it will have an on_shmem_exit callback registered to do that). Rather,
- * this is for subprocesses that have inherited an attachment and want to
- * get rid of it.
- */
- void
- PGSharedMemoryDetach(void)
- {
- if (UsedShmemSegAddr != NULL)
- {
- if ((shmdt(UsedShmemSegAddr) < 0)
- #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
- /* Work-around for cygipc exec bug */
- && shmdt(NULL) < 0
- #endif
- )
- elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
- UsedShmemSegAddr = NULL;
- }
- }
-
-
- /*
- * Attach to shared memory and make sure it has a Postgres header
- *
- * Returns attach address if OK, else NULL
- */
- static PGShmemHeader *
- PGSharedMemoryAttach(IpcMemoryKey key, IpcMemoryId *shmid)
- {
- PGShmemHeader *hdr;
-
- if ((*shmid = shmget(key, sizeof(PGShmemHeader), 0)) < 0)
- return NULL;
-
- hdr = (PGShmemHeader *) shmat(*shmid, UsedShmemSegAddr, PG_SHMAT_FLAGS);
-
- if (hdr == (PGShmemHeader *) -1)
- return NULL; /* failed: must be some other app's */
-
- if (hdr->magic != PGShmemMagic)
- {
- shmdt((void *) hdr);
- return NULL; /* segment belongs to a non-Postgres app */
- }
-
- return hdr;
- }
--- 0 ----
*** a/src/backend/postmaster/autovacuum.c
--- b/src/backend/postmaster/autovacuum.c
***************
*** 368,373 **** StartAutoVacLauncher(void)
--- 368,376 ----
/* Lose the postmaster's on-exit routines */
on_exit_reset();
+ /* Hold on to the data directory lock until this process dies. */
+ AcquireDataDirLock();
+
AutoVacLauncherMain(0, NULL);
break;
#endif
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
***************
*** 632,637 **** pgstat_start(void)
--- 632,640 ----
/* Lose the postmaster's on-exit routines */
on_exit_reset();
+ /* Hold on to the data directory lock for all long as we live.*/
+ AcquireDataDirLock();
+
/* Drop our connection to postmaster's shared memory, as well */
PGSharedMemoryDetach();
*** a/src/backend/postmaster/postmaster.c
--- b/src/backend/postmaster/postmaster.c
***************
*** 484,489 **** PostmasterMain(int argc, char *argv[])
--- 484,490 ----
char *userDoption = NULL;
bool listen_addr_saved = false;
int i;
+ bool blockOnStartupLockOption = false;
MyProcPid = PostmasterPid = getpid();
***************
*** 529,535 **** PostmasterMain(int argc, char *argv[])
* tcop/postgres.c (the option sets should not conflict) and with the
* common help() function in main/main.c.
*/
! while ((opt = getopt(argc, argv, "A:B:c:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:W:-:")) != -1)
{
switch (opt)
{
--- 530,536 ----
* tcop/postgres.c (the option sets should not conflict) and with the
* common help() function in main/main.c.
*/
! while ((opt = getopt(argc, argv, "A:bB:c:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:W:-:")) != -1)
{
switch (opt)
{
***************
*** 537,542 **** PostmasterMain(int argc, char *argv[])
--- 538,546 ----
SetConfigOption("debug_assertions", optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
+ case 'b':
+ blockOnStartupLockOption = true;
+ break;
case 'B':
SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
***************
*** 790,796 **** PostmasterMain(int argc, char *argv[])
* For the same reason, it's best to grab the TCP socket(s) before the
* Unix socket.
*/
! CreateDataDirLockFile(true);
/*
* If timezone is not set, determine what the OS uses. (In theory this
--- 794,800 ----
* For the same reason, it's best to grab the TCP socket(s) before the
* Unix socket.
*/
! CreateDataDirLockFile(true,blockOnStartupLockOption);
/*
* If timezone is not set, determine what the OS uses. (In theory this
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 3600,3606 **** PostgresMain(int argc, char *argv[], const char *username)
/*
* Create lockfile for data directory.
*/
! CreateDataDirLockFile(false);
}
/* Early initialization */
--- 3600,3606 ----
/*
* Create lockfile for data directory.
*/
! CreateDataDirLockFile(false,false);
}
/* Early initialization */
***************
*** 3618,3623 **** PostgresMain(int argc, char *argv[], const char *username)
--- 3618,3633 ----
#else
InitProcess();
#endif
+ if(IsUnderPostmaster)
+ {
+ /* acquire the lock file advisory lock (to eliminate multiple-postmaster race conditions)
+ * postgresql backends (postmaster children) must acquire the read lock to signify that there are backends operating in the specific data directory
+ * this needs to be done after InitProcess because the function needs access to the shared memory proc array
+ */
+ AcquireDataDirLock();
+ }
+
+
/* We need to allow SIGINT, etc during the initial transaction */
PG_SETMASK(&UnBlockSig);
*** a/src/backend/utils/init/miscinit.c
--- b/src/backend/utils/init/miscinit.c
***************
*** 44,57 ****
#include "utils/memutils.h"
#include "utils/syscache.h"
#define DIRECTORY_LOCK_FILE "postmaster.pid"
ProcessingMode Mode = InitProcessing;
- /* Note: we rely on this to initialize as zeroes */
- static char socketLockFile[MAXPGPATH];
-
/* ----------------------------------------------------------------
* ignoring system indexes support stuff
--- 44,75 ----
#include "utils/memutils.h"
#include "utils/syscache.h"
+ /* Note: we rely on this to initialize as zeroes */
+ static char socketLockFile[MAXPGPATH];
#define DIRECTORY_LOCK_FILE "postmaster.pid"
+ static pid_t GetPIDHoldingLock(int fileDescriptor,bool exclusiveLockFlag);
+ static int AcquireLock(int fileDescriptor,bool exclusiveLockFlag,bool waitForLock);
+ static int ReleaseLock(int fileDescriptor);
+ void AcquireDataDirLock();
+ pid_t GetPIDHoldingDataDirLock();
+ static void WriteLockFileContents(char *lockFilePath,int lockFileFD,bool isPostmasterFlag,pid_t processPid,char *dataDirectoryPath,long startTime,int portNumber,char * socketDirectory);
+
+ /* enum used by CreateLockFile to report its success or error condition */
+ typedef enum
+ {
+ /* positive numbers refer to the PID of a conflicting lock-holding process, but not necessarily a postmaster */
+ CreateLockFileNoError=0,
+ CreateLockFileFileError=-1, /*advises the caller to check the errno for the error */
+ CreateLockFileSharedLockAcquisitionError=-2,
+ CreateLockFileExclusiveLockCheckError=-3,
+ } CreateLockFileValue;
+
+
+ static CreateLockFileValue CreateLockFile(char *lockFilePath,bool amPostmaster,int *lockFileRetDescriptor,bool blockOnLockFlag);
ProcessingMode Mode = InitProcessing;
/* ----------------------------------------------------------------
* ignoring system indexes support stuff
***************
*** 627,635 **** GetUserNameFromId(Oid roleid)
/*-------------------------------------------------------------------------
* Interlock-file support
*
! * These routines are used to create both a data-directory lockfile
! * ($DATADIR/postmaster.pid) and a Unix-socket-file lockfile ($SOCKFILE.lock).
! * Both kinds of files contain the same info:
*
* Owning process' PID
* Data directory path
--- 645,653 ----
/*-------------------------------------------------------------------------
* Interlock-file support
*
! * These routines are used to create a data-directory lockfile
! * ($DATADIR/postmaster.pid).
! * The file contains the info:
*
* Owning process' PID
* Data directory path
***************
*** 642,963 **** GetUserNameFromId(Oid roleid)
* A data-directory lockfile can optionally contain a third line, containing
* the key and ID for the shared memory block used by this postmaster.
*
- * On successful lockfile creation, a proc_exit callback to remove the
- * lockfile is automatically created.
*-------------------------------------------------------------------------
*/
! /*
! * proc_exit callback to remove a lockfile.
! */
! static void
! UnlinkLockFile(int status, Datum filename)
! {
! char *fname = (char *) DatumGetPointer(filename);
!
! if (fname != NULL)
! {
! if (unlink(fname) != 0)
! {
! /* Should we complain if the unlink fails? */
! }
! free(fname);
! }
! }
/*
! * Create a lockfile.
*
! * filename is the name of the lockfile to create.
! * amPostmaster is used to determine how to encode the output PID.
! * isDDLock and refName are used to determine what error message to produce.
*/
! static void
! CreateLockFile(const char *filename, bool amPostmaster,
! bool isDDLock, const char *refName)
{
! int fd;
! char buffer[MAXPGPATH * 2 + 256];
! int ntries;
! int len;
! int encoded_pid;
! pid_t other_pid;
! pid_t my_pid,
! my_p_pid,
! my_gp_pid;
! const char *envvar;
!
! /*
! * If the PID in the lockfile is our own PID or our parent's or
! * grandparent's PID, then the file must be stale (probably left over from
! * a previous system boot cycle). We need to check this because of the
! * likelihood that a reboot will assign exactly the same PID as we had in
! * the previous reboot, or one that's only one or two counts larger and
! * hence the lockfile's PID now refers to an ancestor shell process. We
! * allow pg_ctl to pass down its parent shell PID (our grandparent PID)
! * via the environment variable PG_GRANDPARENT_PID; this is so that
! * launching the postmaster via pg_ctl can be just as reliable as
! * launching it directly. There is no provision for detecting
! * further-removed ancestor processes, but if the init script is written
! * carefully then all but the immediate parent shell will be root-owned
! * processes and so the kill test will fail with EPERM. Note that we
! * cannot get a false negative this way, because an existing postmaster
! * would surely never launch a competing postmaster or pg_ctl process
! * directly.
! */
! my_pid = getpid();
!
! #ifndef WIN32
! my_p_pid = getppid();
! #else
!
! /*
! * Windows hasn't got getppid(), but doesn't need it since it's not using
! * real kill() either...
! */
! my_p_pid = 0;
! #endif
!
! envvar = getenv("PG_GRANDPARENT_PID");
! if (envvar)
! my_gp_pid = atoi(envvar);
! else
! my_gp_pid = 0;
!
! /*
! * 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.
! */
! for (ntries = 0;; ntries++)
! {
! /*
! * Try to create the lock file --- O_EXCL makes this atomic.
! *
! * Think not to make the file protection weaker than 0600. See
! * comments below.
! */
! fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
! if (fd >= 0)
! break; /* Success; exit the retry loop */
!
! /*
! * Couldn't create the pid file. Probably it already exists.
! */
! if ((errno != EEXIST && errno != EACCES) || ntries > 100)
! ereport(FATAL,
! (errcode_for_file_access(),
! errmsg("could not create lock file \"%s\": %m",
! filename)));
!
! /*
! * Read the file to get the old owner's PID. Note race condition
! * here: file might have been deleted since we tried to create it.
! */
! fd = open(filename, O_RDONLY, 0600);
! if (fd < 0)
! {
! if (errno == ENOENT)
! continue; /* race condition; try again */
! ereport(FATAL,
! (errcode_for_file_access(),
! errmsg("could not open lock file \"%s\": %m",
! filename)));
! }
! if ((len = read(fd, buffer, sizeof(buffer) - 1)) < 0)
! ereport(FATAL,
! (errcode_for_file_access(),
! errmsg("could not read lock file \"%s\": %m",
! filename)));
! close(fd);
!
! buffer[len] = '\0';
! encoded_pid = atoi(buffer);
!
! /* if pid < 0, the pid is for postgres, not postmaster */
! other_pid = (pid_t) (encoded_pid < 0 ? -encoded_pid : encoded_pid);
!
! if (other_pid <= 0)
! elog(FATAL, "bogus data in lock file \"%s\": \"%s\"",
! filename, buffer);
!
! /*
! * Check to see if the other process still exists
! *
! * Per discussion above, my_pid, my_p_pid, and my_gp_pid can be
! * ignored as false matches.
! *
! * Normally kill() will fail with ESRCH if the given PID doesn't
! * exist.
! *
! * We can treat the EPERM-error case as okay because that error
! * 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
! * 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,
! * we'd have failed above if the lockfile belonged to another userid
! * --- which means that whatever process kill() is reporting about
! * isn't the one that made the lockfile. (NOTE: this last
! * consideration is the only one that keeps us from blowing away a
! * Unix socket file belonging to an instance of Postgres being run by
! * someone else, at least on machines where /tmp hasn't got a
! * stickybit.)
! */
! if (other_pid != my_pid && other_pid != my_p_pid &&
! other_pid != my_gp_pid)
! {
! if (kill(other_pid, 0) == 0 ||
! (errno != ESRCH && errno != EPERM))
! {
! /* lockfile belongs to a live process */
! ereport(FATAL,
! (errcode(ERRCODE_LOCK_FILE_EXISTS),
! errmsg("lock file \"%s\" already exists",
! filename),
! isDDLock ?
! (encoded_pid < 0 ?
! errhint("Is another postgres (PID %d) running in data directory \"%s\"?",
! (int) other_pid, refName) :
! errhint("Is another postmaster (PID %d) running in data directory \"%s\"?",
! (int) other_pid, refName)) :
! (encoded_pid < 0 ?
! errhint("Is another postgres (PID %d) using socket file \"%s\"?",
! (int) other_pid, refName) :
! errhint("Is another postmaster (PID %d) using socket file \"%s\"?",
! (int) other_pid, refName))));
! }
! }
!
! /*
! * 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
! * looking to see if there is an associated shmem segment that is
! * still in use.
! *
! * Note: because postmaster.pid is written in multiple steps, we might
! * not find the shmem ID values in it; we can't treat that as an
! * error.
! */
! if (isDDLock)
! {
! char *ptr = buffer;
! unsigned long id1,
! id2;
! int lineno;
!
! for (lineno = 1; lineno < LOCK_FILE_LINE_SHMEM_KEY; lineno++)
! {
! if ((ptr = strchr(ptr, '\n')) == NULL)
! break;
! ptr++;
! }
!
! if (ptr != NULL &&
! sscanf(ptr, "%lu %lu", &id1, &id2) == 2)
! {
! if (PGSharedMemoryIsInUse(id1, id2))
! ereport(FATAL,
! (errcode(ERRCODE_LOCK_FILE_EXISTS),
! errmsg("pre-existing shared memory block "
! "(key %lu, ID %lu) is still in use",
! id1, id2),
! errhint("If you're sure there are no old "
! "server processes still running, remove "
! "the shared memory block "
! "or just delete the file \"%s\".",
! filename)));
! }
! }
!
! /*
! * Looks like nobody's home. Unlink the file and try again to create
! * it. Need a loop because of possible race condition against other
! * would-be creators.
! */
! if (unlink(filename) < 0)
! ereport(FATAL,
! (errcode_for_file_access(),
! errmsg("could not remove old lock file \"%s\": %m",
! filename),
! errhint("The file seems accidentally left over, but "
! "it could not be removed. Please remove the file "
! "by hand and try again.")));
! }
! /*
! * Successfully created the file, now fill it. See comment in miscadmin.h
! * about the contents. Note that we write the same info into both datadir
! * and socket lockfiles; although more stuff may get added to the datadir
! * lockfile later.
! */
! snprintf(buffer, sizeof(buffer), "%d\n%s\n%ld\n%d\n%s\n",
! amPostmaster ? (int) my_pid : -((int) my_pid),
! DataDir,
! (long) MyStartTime,
! PostPortNumber,
#ifdef HAVE_UNIX_SOCKETS
! (*UnixSocketDir != '\0') ? UnixSocketDir : DEFAULT_PGSOCKET_DIR
#else
! ""
#endif
! );
! errno = 0;
! if (write(fd, buffer, strlen(buffer)) != strlen(buffer))
! {
! int save_errno = errno;
!
! close(fd);
! unlink(filename);
! /* if write didn't set errno, assume problem is no disk space */
! errno = save_errno ? save_errno : ENOSPC;
! ereport(FATAL,
! (errcode_for_file_access(),
! errmsg("could not write lock file \"%s\": %m", filename)));
! }
! if (pg_fsync(fd) != 0)
! {
! int save_errno = errno;
!
! close(fd);
! unlink(filename);
! errno = save_errno;
! ereport(FATAL,
! (errcode_for_file_access(),
! errmsg("could not write lock file \"%s\": %m", filename)));
! }
! if (close(fd) != 0)
! {
! int save_errno = errno;
! unlink(filename);
! errno = save_errno;
! ereport(FATAL,
! (errcode_for_file_access(),
! errmsg("could not write lock file \"%s\": %m", filename)));
! }
! /*
! * Arrange for automatic removal of lockfile at proc_exit.
! */
! on_proc_exit(UnlinkLockFile, PointerGetDatum(strdup(filename)));
}
/*
! * Create the data directory lockfile.
! *
! * When this is called, we must have already switched the working
! * directory to DataDir, so we can just use a relative path. This
! * helps ensure that we are locking the directory we should be.
*/
! void
! CreateDataDirLockFile(bool amPostmaster)
{
! CreateLockFile(DIRECTORY_LOCK_FILE, amPostmaster, true, DataDir);
}
/*
--- 660,888 ----
* A data-directory lockfile can optionally contain a third line, containing
* the key and ID for the shared memory block used by this postmaster.
*
*-------------------------------------------------------------------------
*/
! /* We hold onto the lockFile for the life of the process to hold onto the advisory locks. */
! static int DataDirLockFileFD = 0;
/*
! * Create the data directory lockfile.
*
! * When this is called, we must have already switched the working
! * directory to DataDir, so we can just use a relative path. This
! * helps ensure that we are locking the directory we should be.
*/
! void
! CreateDataDirLockFile(bool amPostmaster,bool blockOptionFlag)
{
! char *lockFilePath=DIRECTORY_LOCK_FILE;
! CreateLockFileValue error = CreateLockFile(lockFilePath,amPostmaster,&DataDirLockFileFD,blockOptionFlag);
! if(error==CreateLockFileNoError)
! {
! return;
! }
! else if(error==CreateLockFileFileError)
! {
! ereport(FATAL,
! (errmsg("failed operation on lock file at \"%s\": %m",lockFilePath)));
! }
! else if(error==CreateLockFileSharedLockAcquisitionError)
! {
! ereport(FATAL,
! (errmsg("failed to acquire shared lock on file \"%s\": %m",lockFilePath)));
! }
! else if(error==CreateLockFileExclusiveLockCheckError)
! {
! ereport(FATAL,
! (errmsg("failed to check for exclusive lock on file \"%s\": %m", lockFilePath)));
!
! }
! else if(error>0)
! {
! /* error holds the pid of the conflicting process */
! ereport(FATAL,
! (errmsg("another postgresql process is running in the data directory \"%s\" with pid %d",DataDir,error),
! errhint("kill the other server processes to start a new postgresql server")));
! }
! else
! {
! ereport(FATAL,
! (errmsg("an unhandled locking error occurred")));
! }
! }
! static CreateLockFileValue
! CreateLockFile(char *lockFilePath,bool amPostmaster,int *lockFileRetDescriptor,bool blockOnLockFlag)
! {
! int success = 0;
! pid_t pidHoldingLock;
! int lockFileFD = 0;
!
! /* open the directory lock file- whether or not it already exists is irrelevant because we will check for file locks */
! lockFileFD = open(lockFilePath, O_RDWR | O_CREAT, 0600);
! if(lockFileFD<0)
! {
! return CreateLockFileFileError;
! }
!
! if(!blockOnLockFlag)
! {
! /* Acquire the shared advisory lock without blocking*/
!
! success = AcquireLock(lockFileFD,false,false);
! if(success < 0)
! {
! /* We failed to acquire the read lock, which is unlikely because we no one should be holding an exclusive lock on it. */
! return CreateLockFileSharedLockAcquisitionError;
! }
! }
! else
! {
! /* loop until we get the exclusive lock and the subsequent shared lock- waiting until the data directory is not being serviced is data directory postgresql hot standy mode*/
! while(1)
! {
! success = AcquireLock(lockFileFD,true,true);
! if(success < 0)
! return CreateLockFileExclusiveLockCheckError;
!
! /* now we hold the exclusive lock, so demote to read lock, but be wary of the race condition whereby a different postmaster could also be waiting to grab the read lock too */
! success = ReleaseLock(lockFileFD);
! if(success < 0)
! return CreateLockFileExclusiveLockCheckError;
!
! success = AcquireLock(lockFileFD,false,false);
! if(success < 0)
! {
! /* d'oh- some other postmaster grabbed the exclusive lock in the meantime, so try again later */
! pg_usleep(500L);
! continue;
! }
! else
! break;
! }
! }
!
!
! /* Determine if acquiring an exclusive (write) lock would be denied. If so, there is another postmaster or postgres child process running, so abort. */
! pidHoldingLock = GetPIDHoldingLock(lockFileFD,true);
! if(pidHoldingLock < 0)
! {
! /* checking for a lock failed */
! return CreateLockFileExclusiveLockCheckError;
! }
! else if(pidHoldingLock > 0)
! {
! /* there is another process holding the lock, so we must abort starting a new postmaster */
! return pidHoldingLock;
! }
! /*no process would block the lock, so we are cleared for starting a new postmaster*/
! WriteLockFileContents(lockFilePath,lockFileFD,
! true,
! getpid(),
! DataDir,
! (long)MyStartTime,
! PostPortNumber,
#ifdef HAVE_UNIX_SOCKETS
! (*UnixSocketDir != '\0') ? UnixSocketDir : DEFAULT_PGSOCKET_DIR
#else
! ""
#endif
! );
! /* There is no need to remove the lock file because the locks synchronize access, not the existence of the file. */
! if(lockFileRetDescriptor != NULL)
! *lockFileRetDescriptor = lockFileFD;
! return CreateLockFileNoError;
! }
! static void WriteLockFileContents(char *lockFilePath,int lockFileFD,bool isPostmasterFlag,pid_t processPid,char *dataDirectoryPath,long startTime,int portNumber,char * socketDirectoryPath)
! {
! char writeBuffer[MAXPGPATH * 2 + 256];
! snprintf(writeBuffer,sizeof(writeBuffer),"%d\n%s\n%ld\n%d\n%s\n",
! isPostmasterFlag ? (int) processPid : -((int) processPid),
! dataDirectoryPath,
! startTime,
! portNumber,
! socketDirectoryPath
! );
! errno = 0;
! if (write(lockFileFD, writeBuffer, strlen(writeBuffer)) != strlen(writeBuffer))
! {
! int save_errno = errno;
! unlink(lockFilePath);
! /* if write didn't set errno, assume problem is no disk space */
! errno = save_errno ? save_errno : ENOSPC;
! ereport(FATAL,
! (errcode_for_file_access(),
! errmsg("could not write lock file \"%s\": %m", lockFilePath)));
! }
! if (pg_fsync(lockFileFD) != 0)
! {
! int save_errno = errno;
!
! unlink(lockFilePath);
! errno = save_errno;
! ereport(FATAL,
! (errcode_for_file_access(),
! errmsg("could not write lock file \"%s\": %m", lockFilePath)));
! }
! return;
! }
! /* Called by pg_ctl to determine when the postmaster is shutdown. */
! pid_t GetPIDHoldingDataDirLock(void)
! {
! return GetPIDHoldingLock(DataDirLockFileFD,true);
}
+
/*
! * Called by backends when they startup to signify that the data directory is in use
*/
! void AcquireDataDirLock(void)
{
! int success;
! int exclusiveLockViolatingPID = 0;
! /* get the read lock */
! success = AcquireLock(DataDirLockFileFD,false,false);
! if(success < 0)
! {
! /* Failed to acquire read lock, bomb out */
! ereport(FATAL,
! (errmsg("failed to acquire lock on \"%s\": %m",DIRECTORY_LOCK_FILE)));
!
! }
! /* verify that grabbing an exclusive lock would complain that the parent or PROC_ARRAY sibling process would cause exclusive lock acquisition to fail- otherwise a separate postmaster is holding the lock (eliminates a possible race condition when the postmaster spawns a backend, immediately dies and new postmaster takes over) */
! exclusiveLockViolatingPID = GetPIDHoldingLock(DataDirLockFileFD,true);
! if(exclusiveLockViolatingPID < 0)
! {
! /* error testing for the lock, very unlikely, but fatal */
! ereport(FATAL,
! (errmsg("failed to test for lock on \"%s\": %m",DIRECTORY_LOCK_FILE)));
! }
! else if(exclusiveLockViolatingPID == 0)
! {
! /* the postmaster should be holding the lock- in this case it is not (and this is the only backend running), so don't bother running the backend because the postmaster just died */
! ereport(FATAL,
! (errmsg("failed to initialize backend because the postmaster exited")));
! }
! else
! {
! /* the PID is valid, so we should check that the PID refers either to the postmaster or its children */
! /* NOTE TO REVIEWER: is this too early to call BackendPidGetProc? */
! PGPROC *violatingProc = NULL;
! violatingProc = BackendPidGetProc(exclusiveLockViolatingPID);
!
! if(exclusiveLockViolatingPID != getppid() &&
! violatingProc == NULL)
! {
! /* the violating lock is neither the postmaster nor a sibling child- data directory conflict detected! */
! ereport(FATAL,
! (errmsg("backend startup race condition detected- another postmaster is running in this data directory")));
! }
! }
! return;
}
/*
***************
*** 966,977 **** CreateDataDirLockFile(bool amPostmaster)
void
CreateSocketLockFile(const char *socketfile, bool amPostmaster)
{
! char lockfile[MAXPGPATH];
!
! snprintf(lockfile, sizeof(lockfile), "%s.lock", socketfile);
! CreateLockFile(lockfile, amPostmaster, false, socketfile);
! /* Save name of lockfile for TouchSocketLockFile */
! strcpy(socketLockFile, lockfile);
}
/*
--- 891,939 ----
void
CreateSocketLockFile(const char *socketfile, bool amPostmaster)
{
! char lockFilePath[MAXPGPATH];
! CreateLockFileValue error;
!
! snprintf(lockFilePath, sizeof(lockFilePath), "%s.lock", socketfile);
!
! /* This intentionally leaks the socket file descriptor- we hold onto it so that the lock is held until the process is exited */
! error = CreateLockFile(lockFilePath, amPostmaster,NULL,false);
!
! if(error==CreateLockFileNoError)
! {
! return;
! }
! else if(error==CreateLockFileFileError)
! {
! ereport(FATAL,
! (errmsg("failed operation on lock file at \"%s\": %m",lockFilePath)));
! }
! else if(error==CreateLockFileSharedLockAcquisitionError)
! {
! ereport(FATAL,
! (errmsg("failed to acquire shared lock on file \"%s\": %m",lockFilePath)));
! }
! else if(error==CreateLockFileExclusiveLockCheckError)
! {
! ereport(FATAL,
! (errmsg("failed to check for exclusive lock on file \"%s\": %m", lockFilePath)));
!
! }
! else if(error>0)
! {
! /* error holds the pid of the conflicting process */
! ereport(FATAL,
! (errmsg("another postgresql process with pid %d is bound to the socket file at \"%s\"",error,lockFilePath),
! errhint("configure a different socket file path in postgresql.conf or kill the conflicting postgresql server")));
! }
! else
! {
! ereport(FATAL,
! (errmsg("an unhandled locking error occurred")));
! }
!
! /* Save name of lockfile for TouchSocketLockFile */
! strcpy(socketLockFile, lockFilePath);
}
/*
***************
*** 985,1020 **** CreateSocketLockFile(const char *socketfile, bool amPostmaster)
void
TouchSocketLockFile(void)
{
! /* Do nothing if we did not create a socket... */
! if (socketLockFile[0] != '\0')
! {
! /*
! * utime() is POSIX standard, utimes() is a common alternative; if we
! * have neither, fall back to actually reading the file (which only
! * sets the access time not mod time, but that should be enough in
! * most cases). In all paths, we ignore errors.
! */
#ifdef HAVE_UTIME
! utime(socketLockFile, NULL);
! #else /* !HAVE_UTIME */
#ifdef HAVE_UTIMES
! utimes(socketLockFile, NULL);
! #else /* !HAVE_UTIMES */
! int fd;
! char buffer[1];
!
! fd = open(socketLockFile, O_RDONLY | PG_BINARY, 0);
! if (fd >= 0)
! {
! read(fd, buffer, sizeof(buffer));
! close(fd);
! }
#endif /* HAVE_UTIMES */
#endif /* HAVE_UTIME */
! }
}
-
/*
* Add (or replace) a line in the data directory lock file.
* The given string should not include a trailing newline.
--- 947,980 ----
void
TouchSocketLockFile(void)
{
! /* Do nothing if we did not create a socket... */
! if (socketLockFile[0] != '\0')
! {
! /*
! * utime() is POSIX standard, utimes() is a common alternative; if we
! * have neither, fall back to actually reading the file (which only
! * sets the access time not mod time, but that should be enough in
! * most cases). In all paths, we ignore errors.
! */
#ifdef HAVE_UTIME
! utime(socketLockFile, NULL);
! #else/* !HAVE_UTIME */
#ifdef HAVE_UTIMES
! utimes(socketLockFile, NULL);
! #else/* !HAVE_UTIMES */
! intfd;
! charbuffer[1];
!
! fd = open(socketLockFile, O_RDONLY | PG_BINARY, 0);
! if (fd >= 0)
! {
! read(fd, buffer, sizeof(buffer));
! }
#endif /* HAVE_UTIMES */
#endif /* HAVE_UTIME */
! }
}
/*
* Add (or replace) a line in the data directory lock file.
* The given string should not include a trailing newline.
***************
*** 1030,1043 **** AddToDataDirLockFile(int target_line, const char *str)
int lineno;
char *ptr;
char buffer[BLCKSZ];
! fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
! if (fd < 0)
{
ereport(LOG,
(errcode_for_file_access(),
! errmsg("could not open file \"%s\": %m",
! DIRECTORY_LOCK_FILE)));
return;
}
len = read(fd, buffer, sizeof(buffer) - 1);
--- 990,1006 ----
int lineno;
char *ptr;
char buffer[BLCKSZ];
+ int success;
! fd = DataDirLockFileFD;
! /* rewind the file handle to rewrite it */
! success = lseek(fd,0,SEEK_SET);
! if (success < 0)
{
ereport(LOG,
(errcode_for_file_access(),
! errmsg("could not seek lock file \"%s\": %m",
! DIRECTORY_LOCK_FILE)));
return;
}
len = read(fd, buffer, sizeof(buffer) - 1);
***************
*** 1047,1053 **** AddToDataDirLockFile(int target_line, const char *str)
(errcode_for_file_access(),
errmsg("could not read from file \"%s\": %m",
DIRECTORY_LOCK_FILE)));
- close(fd);
return;
}
buffer[len] = '\0';
--- 1010,1015 ----
***************
*** 1061,1067 **** AddToDataDirLockFile(int target_line, const char *str)
if ((ptr = strchr(ptr, '\n')) == NULL)
{
elog(LOG, "bogus data in \"%s\"", DIRECTORY_LOCK_FILE);
- close(fd);
return;
}
ptr++;
--- 1023,1028 ----
***************
*** 1088,1094 **** AddToDataDirLockFile(int target_line, const char *str)
(errcode_for_file_access(),
errmsg("could not write to file \"%s\": %m",
DIRECTORY_LOCK_FILE)));
- close(fd);
return;
}
if (pg_fsync(fd) != 0)
--- 1049,1054 ----
***************
*** 1098,1110 **** AddToDataDirLockFile(int target_line, const char *str)
errmsg("could not write to file \"%s\": %m",
DIRECTORY_LOCK_FILE)));
}
- if (close(fd) != 0)
- {
- ereport(LOG,
- (errcode_for_file_access(),
- errmsg("could not write to file \"%s\": %m",
- DIRECTORY_LOCK_FILE)));
- }
}
--- 1058,1063 ----
***************
*** 1300,1302 **** pg_bindtextdomain(const char *domain)
--- 1253,1300 ----
}
#endif
}
+
+ /* We can also offer the option to block until the other postmaster is cleared away using F_SETLKW */
+ static int AcquireLock(int fileDescriptor,bool exclusiveLockFlag,bool waitForLock)
+ {
+ struct flock lock = {
+ .l_type = exclusiveLockFlag ? F_WRLCK : F_RDLCK,
+ .l_start = 0,
+ .l_whence = SEEK_SET,
+ .l_len = 100
+ };
+ return fcntl(fileDescriptor , waitForLock ? F_SETLKW : F_SETLK, &lock);
+ }
+
+ static int ReleaseLock(int fileDescriptor)
+ {
+ struct flock lock = {
+ .l_type = F_UNLCK,
+ .l_start = 0,
+ .l_whence = SEEK_SET,
+ .l_len = 100
+ };
+ return fcntl(fileDescriptor, F_SETLK, &lock);
+ }
+
+ static pid_t GetPIDHoldingLock(int fileDescriptor,bool exclusiveLockFlag)
+ {
+ struct flock lock = {
+ .l_type = exclusiveLockFlag ? F_WRLCK : F_RDLCK,
+ .l_start = 0,
+ .l_whence = SEEK_SET,
+ .l_len = 100,
+ .l_pid = 0
+ };
+ int success;
+
+ success = fcntl(fileDescriptor,F_GETLK,&lock);
+ if(success < 0)
+ {
+ return (pid_t)success;
+ }
+ if(lock.l_whence == SEEK_SET)
+ return lock.l_pid;
+ else
+ return -1;
+ }
*** a/src/bin/pg_ctl/pg_ctl.c
--- b/src/bin/pg_ctl/pg_ctl.c
***************
*** 26,31 ****
--- 26,32 ----
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
+ #include <fcntl.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/time.h>
***************
*** 271,297 **** get_pgpid(void)
{
FILE *pidf;
long pid;
pidf = fopen(pid_file, "r");
! if (pidf == NULL)
! {
! /* No pid file, not an error on startup */
! if (errno == ENOENT)
! return 0;
! else
! {
! write_stderr(_("%s: could not open PID file \"%s\": %s\n"),
! progname, pid_file, strerror(errno));
! exit(1);
! }
! }
! if (fscanf(pidf, "%ld", &pid) != 1)
! {
! write_stderr(_("%s: invalid data in PID file \"%s\"\n"),
! progname, pid_file);
! exit(1);
! }
fclose(pidf);
return (pgpid_t) pid;
}
--- 272,320 ----
{
FILE *pidf;
long pid;
+ struct flock lock = {
+ .l_type = F_WRLCK,
+ .l_start = 0,
+ .l_whence = SEEK_SET,
+ .l_len = 100,
+ .l_pid = 0
+ };
+ int success;
pidf = fopen(pid_file, "r");
!
! /* Attempt to acquire an exclusive lock. If that fails, we know that an existing backend is still holding a read lock. See src/backend/utils/init/miscinit.c for more details. */
! if(pidf == NULL)
! {
! if(errno == ENOENT)
! {
! /* No lock file found. */
! return 0;
! }
! else
! {
! write_stderr(_("%s: could not open PID file \"%s\": %s\n"),
! progname,pid_file,strerror(errno));
! exit(1);
! }
! }
! success = fcntl(fileno(pidf),F_GETLK,&lock);
fclose(pidf);
+ if(success < 0)
+ {
+ /* Failed syscall */
+ write_stderr(_("%s: failed to test lock status: %s"),progname,strerror(errno));
+ exit(1);
+ }
+ if(lock.l_whence == SEEK_SET)
+ /* There is a pid holding a lock. */
+ return lock.l_pid;
+ else if(lock.l_type == F_UNLCK)
+ /* No lock would block exclusive access. */
+ return 0;
+ else
+ return -1;
+
return (pgpid_t) pid;
}
*** a/src/include/miscadmin.h
--- b/src/include/miscadmin.h
***************
*** 373,379 **** extern char *local_preload_libraries_string;
#define LOCK_FILE_LINE_LISTEN_ADDR 6
#define LOCK_FILE_LINE_SHMEM_KEY 7
! extern void CreateDataDirLockFile(bool amPostmaster);
extern void CreateSocketLockFile(const char *socketfile, bool amPostmaster);
extern void TouchSocketLockFile(void);
extern void AddToDataDirLockFile(int target_line, const char *str);
--- 373,381 ----
#define LOCK_FILE_LINE_LISTEN_ADDR 6
#define LOCK_FILE_LINE_SHMEM_KEY 7
! extern void CreateDataDirLockFile(bool amPostmaster,bool blockOptionFlag);
! extern void AcquireDataDirLock(void);
! extern pid_t GetPIDHoldingDataDirLock(void);
extern void CreateSocketLockFile(const char *socketfile, bool amPostmaster);
extern void TouchSocketLockFile(void);
extern void AddToDataDirLockFile(int target_line, const char *str);