You've already forked pgbackrest
							
							
				mirror of
				https://github.com/pgbackrest/pgbackrest.git
				synced 2025-10-30 23:37:45 +02:00 
			
		
		
		
	The STRING_CONST() macro worked fine for constants but was not able to constify strings created at runtime. Add the STR() macro to do this by using strlen() to get the size. Also rename STRING_CONST() to STRDEF() for brevity and to match the other macro name.
		
			
				
	
	
		
			281 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			281 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***********************************************************************************************************************************
 | |
| Lock Handler
 | |
| ***********************************************************************************************************************************/
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <string.h>
 | |
| #include <sys/file.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #include "common/debug.h"
 | |
| #include "common/io/handleWrite.h"
 | |
| #include "common/lock.h"
 | |
| #include "common/log.h"
 | |
| #include "common/memContext.h"
 | |
| #include "common/wait.h"
 | |
| #include "storage/helper.h"
 | |
| #include "storage/storage.intern.h"
 | |
| #include "version.h"
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Lock type names
 | |
| ***********************************************************************************************************************************/
 | |
| static const char *lockTypeName[] =
 | |
| {
 | |
|     "archive",                                                      // lockTypeArchive
 | |
|     "backup",                                                       // lockTypeBackup
 | |
| };
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Mem context and local variables
 | |
| ***********************************************************************************************************************************/
 | |
| static MemContext *lockMemContext = NULL;
 | |
| static String *lockFile[lockTypeAll];
 | |
| static int lockHandle[lockTypeAll];
 | |
| static LockType lockTypeHeld = lockTypeNone;
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Acquire a lock using a file on the local filesystem
 | |
| ***********************************************************************************************************************************/
 | |
| static int
 | |
| lockAcquireFile(const String *lockFile, TimeMSec lockTimeout, bool failOnNoLock)
 | |
| {
 | |
|     FUNCTION_LOG_BEGIN(logLevelTrace);
 | |
|         FUNCTION_LOG_PARAM(STRING, lockFile);
 | |
|         FUNCTION_LOG_PARAM(TIMEMSEC, lockTimeout);
 | |
|         FUNCTION_LOG_PARAM(BOOL, failOnNoLock);
 | |
|     FUNCTION_LOG_END();
 | |
| 
 | |
|     int result = -1;
 | |
| 
 | |
|     MEM_CONTEXT_TEMP_BEGIN()
 | |
|     {
 | |
|         Wait *wait = lockTimeout != 0 ? waitNew(lockTimeout) : NULL;
 | |
|         bool retry = false;
 | |
|         int errNo = 0;
 | |
| 
 | |
|         do
 | |
|         {
 | |
|             // Attempt to open the file
 | |
|             if ((result = open(strPtr(lockFile), O_WRONLY | O_CREAT, STORAGE_MODE_FILE_DEFAULT)) == -1)
 | |
|             {
 | |
|                 // Save the error for reporting outside the loop
 | |
|                 errNo = errno;
 | |
| 
 | |
|                 // If the path does not exist then create it
 | |
|                 if (errNo == ENOENT)
 | |
|                 {
 | |
|                     storagePathCreateNP(storageLocalWrite(), strPath(lockFile));
 | |
|                     retry = true;
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // Attempt to lock the file
 | |
|                 if (flock(result, LOCK_EX | LOCK_NB) == -1)
 | |
|                 {
 | |
|                     // Save the error for reporting outside the loop
 | |
|                     errNo = errno;
 | |
| 
 | |
|                     // Close the file and reset the handle
 | |
|                     close(result);
 | |
|                     result = -1;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         while (result == -1 && ((wait != NULL && waitMore(wait)) || retry));
 | |
| 
 | |
|         waitFree(wait);
 | |
| 
 | |
|         // If the lock was not successful
 | |
|         if (result == -1)
 | |
|         {
 | |
|             // Error when requested
 | |
|             if (failOnNoLock)
 | |
|             {
 | |
|                 const String *errorHint = NULL;
 | |
| 
 | |
|                 if (errNo == EWOULDBLOCK)
 | |
|                     errorHint = STRDEF("\nHINT: is another " PROJECT_NAME " process running?");
 | |
|                 else if (errNo == EACCES)
 | |
|                 {
 | |
|                     errorHint = strNewFmt(
 | |
|                         "\nHINT: does the user running " PROJECT_NAME " have permissions on the '%s' file?",
 | |
|                         strPtr(lockFile));
 | |
|                 }
 | |
| 
 | |
|                 THROW_FMT(
 | |
|                     LockAcquireError, "unable to acquire lock on file '%s': %s%s",
 | |
|                     strPtr(lockFile), strerror(errNo), errorHint == NULL ? "" : strPtr(errorHint));
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             // Write pid of the current process
 | |
|             ioHandleWriteOneStr(result, strNewFmt("%d\n", getpid()));
 | |
|         }
 | |
|     }
 | |
|     MEM_CONTEXT_TEMP_END();
 | |
| 
 | |
|     FUNCTION_LOG_RETURN(INT, result);
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Release the current lock
 | |
| ***********************************************************************************************************************************/
 | |
| static void
 | |
| lockReleaseFile(int lockHandle, const String *lockFile)
 | |
| {
 | |
|     FUNCTION_LOG_BEGIN(logLevelTrace);
 | |
|         FUNCTION_LOG_PARAM(INT, lockHandle);
 | |
|         FUNCTION_LOG_PARAM(STRING, lockFile);
 | |
|     FUNCTION_LOG_END();
 | |
| 
 | |
|     // Can't release lock if there isn't one
 | |
|     ASSERT(lockHandle != -1);
 | |
| 
 | |
|     // Remove file first and then close it to release the lock.  If we close it first then another process might grab the lock
 | |
|     // right before the delete which means the file locked by the other process will get deleted.
 | |
|     storageRemoveNP(storageLocalWrite(), lockFile);
 | |
|     close(lockHandle);
 | |
| 
 | |
|     FUNCTION_LOG_RETURN_VOID();
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Acquire a lock type
 | |
| 
 | |
| This will involve locking one or more files on disk depending on the lock type.  Most operations only take a single lock (archive or
 | |
| backup), but the stanza commands all need to lock both.
 | |
| ***********************************************************************************************************************************/
 | |
| bool
 | |
| lockAcquire(const String *lockPath, const String *stanza, LockType lockType, TimeMSec lockTimeout, bool failOnNoLock)
 | |
| {
 | |
|     FUNCTION_LOG_BEGIN(logLevelDebug);
 | |
|         FUNCTION_LOG_PARAM(STRING, lockPath);
 | |
|         FUNCTION_LOG_PARAM(STRING, stanza);
 | |
|         FUNCTION_LOG_PARAM(ENUM, lockType);
 | |
|         FUNCTION_LOG_PARAM(TIMEMSEC, lockTimeout);
 | |
|         FUNCTION_LOG_PARAM(BOOL, failOnNoLock);
 | |
|     FUNCTION_LOG_END();
 | |
| 
 | |
|     bool result = false;
 | |
| 
 | |
|     // Don't allow failures when locking more than one file.  This makes cleanup difficult and there are no known use cases.
 | |
|     ASSERT(failOnNoLock || lockType != lockTypeAll);
 | |
| 
 | |
|     // Don't allow another lock if one is already held
 | |
|     if (lockTypeHeld != lockTypeNone)
 | |
|         THROW(AssertError, "lock is already held by this process");
 | |
| 
 | |
|     // Allocate a mem context to hold lock filenames if one does not already exist
 | |
|     if (lockMemContext == NULL)
 | |
|     {
 | |
|         MEM_CONTEXT_BEGIN(memContextTop())
 | |
|         {
 | |
|             lockMemContext = memContextNew("Lock");
 | |
|         }
 | |
|         MEM_CONTEXT_END();
 | |
|     }
 | |
| 
 | |
|     // Lock files
 | |
|     MEM_CONTEXT_BEGIN(lockMemContext)
 | |
|     {
 | |
|         LockType lockMin = lockType == lockTypeAll ? lockTypeArchive : lockType;
 | |
|         LockType lockMax = lockType == lockTypeAll ? (lockTypeAll - 1) : lockType;
 | |
|         bool error = false;
 | |
| 
 | |
|         for (LockType lockIdx = lockMin; lockIdx <= lockMax; lockIdx++)
 | |
|         {
 | |
|             lockFile[lockIdx] = strNewFmt("%s/%s-%s.lock", strPtr(lockPath), strPtr(stanza), lockTypeName[lockIdx]);
 | |
| 
 | |
|             lockHandle[lockIdx] = lockAcquireFile(lockFile[lockIdx], lockTimeout, failOnNoLock);
 | |
| 
 | |
|             if (lockHandle[lockIdx] == -1)
 | |
|             {
 | |
|                 error = true;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (!error)
 | |
|         {
 | |
|             lockTypeHeld = lockType;
 | |
|             result = true;
 | |
|         }
 | |
|     }
 | |
|     MEM_CONTEXT_END();
 | |
| 
 | |
|     FUNCTION_LOG_RETURN(BOOL, result);
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Clear the lock without releasing it.  This is used by a master process after it has spawned a child so the child can keep the lock
 | |
| and the master process won't try to free it.
 | |
| ***********************************************************************************************************************************/
 | |
| bool
 | |
| lockClear(bool failOnNoLock)
 | |
| {
 | |
|     FUNCTION_LOG_BEGIN(logLevelTrace);
 | |
|         FUNCTION_LOG_PARAM(BOOL, failOnNoLock);
 | |
|     FUNCTION_LOG_END();
 | |
| 
 | |
|     bool result = false;
 | |
| 
 | |
|     if (lockTypeHeld == lockTypeNone)
 | |
|     {
 | |
|         if (failOnNoLock)
 | |
|             THROW(AssertError, "no lock is held by this process");
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         // Clear locks
 | |
|         LockType lockMin = lockTypeHeld == lockTypeAll ? lockTypeArchive : lockTypeHeld;
 | |
|         LockType lockMax = lockTypeHeld == lockTypeAll ? (lockTypeAll - 1) : lockTypeHeld;
 | |
| 
 | |
|         for (LockType lockIdx = lockMin; lockIdx <= lockMax; lockIdx++)
 | |
|             strFree(lockFile[lockIdx]);
 | |
| 
 | |
|         lockTypeHeld = lockTypeNone;
 | |
|         result = true;
 | |
|     }
 | |
| 
 | |
|     FUNCTION_LOG_RETURN(BOOL, result);
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Release a lock type
 | |
| ***********************************************************************************************************************************/
 | |
| bool
 | |
| lockRelease(bool failOnNoLock)
 | |
| {
 | |
|     FUNCTION_LOG_BEGIN(logLevelDebug);
 | |
|         FUNCTION_LOG_PARAM(BOOL, failOnNoLock);
 | |
|     FUNCTION_LOG_END();
 | |
| 
 | |
|     bool result = false;
 | |
| 
 | |
|     if (lockTypeHeld == lockTypeNone)
 | |
|     {
 | |
|         if (failOnNoLock)
 | |
|             THROW(AssertError, "no lock is held by this process");
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         // Release locks
 | |
|         LockType lockMin = lockTypeHeld == lockTypeAll ? lockTypeArchive : lockTypeHeld;
 | |
|         LockType lockMax = lockTypeHeld == lockTypeAll ? (lockTypeAll - 1) : lockTypeHeld;
 | |
| 
 | |
|         for (LockType lockIdx = lockMin; lockIdx <= lockMax; lockIdx++)
 | |
|         {
 | |
|             lockReleaseFile(lockHandle[lockIdx], lockFile[lockIdx]);
 | |
|             strFree(lockFile[lockIdx]);
 | |
|         }
 | |
| 
 | |
|         lockTypeHeld = lockTypeNone;
 | |
|         result = true;
 | |
|     }
 | |
| 
 | |
|     FUNCTION_LOG_RETURN(BOOL, result);
 | |
| }
 |