Bug 151188. Use fcntl for profile locking on Linux/Unix, with symlinks as a backup. We'll continue to also take the symlink, so that older builds are still locked out, but we mark the symlink so that if a newer fcntl-build leaves a stale symlink lock we detect and remove it.

This commit is contained in:
roc+%cs.cmu.edu 2005-08-02 21:30:48 +00:00
parent 9503227fcd
commit be9e025632
2 changed files with 104 additions and 75 deletions

View File

@ -253,7 +253,58 @@ nsresult nsProfileLock::LockWithFcntl(const nsACString& lockFilePath)
return rv;
}
nsresult nsProfileLock::LockWithSymlink(const nsACString& lockFilePath)
static PRBool IsSymlinkStaleLock(struct in_addr* aAddr, const char* aFileName,
PRBool aHaveFcntlLock)
{
// the link exists; see if it's from this machine, and if
// so if the process is still active
char buf[1024];
int len = readlink(aFileName, buf, sizeof buf - 1);
if (len > 0)
{
buf[len] = '\0';
char *colon = strchr(buf, ':');
if (colon)
{
*colon++ = '\0';
unsigned long addr = inet_addr(buf);
if (addr != (unsigned long) -1)
{
if (colon[0] == '+' && aHaveFcntlLock) {
// This lock was placed by a Firefox build which would have
// taken the fnctl lock, and we've already taken the fcntl lock,
// so the process that created this obsolete lock must be gone
return PR_TRUE;
}
char *after = nsnull;
pid_t pid = strtol(colon, &after, 0);
if (pid != 0 && *after == '\0')
{
if (addr != aAddr->s_addr)
{
// Remote lock: give up even if stuck.
return PR_FALSE;
}
// kill(pid,0) is a neat trick to check if a
// process exists
if (kill(pid, 0) == 0 || errno != ESRCH)
{
// Local process appears to be alive, ass-u-me it
// is another Mozilla instance, or a compatible
// derivative, that's currently using the profile.
// XXX need an "are you Mozilla?" protocol
return PR_FALSE;
}
}
}
}
}
return PR_TRUE;
}
nsresult nsProfileLock::LockWithSymlink(const nsACString& lockFilePath, PRBool aHaveFcntlLock)
{
nsresult rv;
@ -272,7 +323,8 @@ nsresult nsProfileLock::LockWithSymlink(const nsACString& lockFilePath)
}
char *signature =
PR_smprintf("%s:%lu", inet_ntoa(inaddr), (unsigned long)getpid());
PR_smprintf("%s:%s%lu", inet_ntoa(inaddr), aHaveFcntlLock ? "+" : "",
(unsigned long)getpid());
const nsPromiseFlatCString& flat = PromiseFlatCString(lockFilePath);
const char *fileName = flat.get();
int symlink_rv, symlink_errno, tries = 0;
@ -284,44 +336,8 @@ nsresult nsProfileLock::LockWithSymlink(const nsACString& lockFilePath)
if (symlink_errno != EEXIST)
break;
// the link exists; see if it's from this machine, and if
// so if the process is still active
char buf[1024];
int len = readlink(fileName, buf, sizeof buf - 1);
if (len > 0)
{
buf[len] = '\0';
char *colon = strchr(buf, ':');
if (colon)
{
*colon++ = '\0';
unsigned long addr = inet_addr(buf);
if (addr != (unsigned long) -1)
{
char *after = nsnull;
pid_t pid = strtol(colon, &after, 0);
if (pid != 0 && *after == '\0')
{
if (addr != inaddr.s_addr)
{
// Remote lock: give up even if stuck.
break;
}
// kill(pid,0) is a neat trick to check if a
// process exists
if (kill(pid, 0) == 0 || errno != ESRCH)
{
// Local process appears to be alive, ass-u-me it
// is another Mozilla instance, or a compatible
// derivative, that's currently using the profile.
// XXX need an "are you Mozilla?" protocol
break;
}
}
}
}
}
if (!IsSymlinkStaleLock(&inaddr, fileName, aHaveFcntlLock))
break;
// Lock seems to be bogus: try to claim it. Give up after a large
// number of attempts (100 comes from the 4.x codebase).
@ -424,7 +440,7 @@ nsresult nsProfileLock::Lock(nsILocalFile* aProfileDir,
rv = lockFile->Append(LOCKFILE_NAME);
if (NS_FAILED(rv))
return rv;
#if defined(XP_MACOSX)
// First, try locking using fcntl. It is more reliable on
// a local machine, but may not be supported by an NFS server.
@ -432,13 +448,15 @@ nsresult nsProfileLock::Lock(nsILocalFile* aProfileDir,
rv = lockFile->GetNativePath(filePath);
if (NS_FAILED(rv))
return rv;
rv = LockWithFcntl(filePath);
if (NS_FAILED(rv) && (rv != NS_ERROR_FILE_ACCESS_DENIED))
{
// If that failed for any reason other than NS_ERROR_FILE_ACCESS_DENIED,
// assume we tried an NFS that does not support it. Now, try with symlink.
rv = LockWithSymlink(filePath);
rv = LockWithSymlink(filePath, PR_FALSE);
}
if (NS_SUCCEEDED(rv))
{
// Check for the old-style lock used by pre-mozilla 1.3 builds.
@ -483,6 +501,43 @@ nsresult nsProfileLock::Lock(nsILocalFile* aProfileDir,
}
rv = NS_OK; // Don't propagate error from OpenNSPRFileDesc.
}
#elif defined(XP_UNIX)
nsCAutoString filePath;
rv = lockFile->GetNativePath(filePath);
if (NS_FAILED(rv))
return rv;
// Get the old lockfile name
nsCOMPtr<nsIFile> oldLockFile;
rv = aProfileDir->Clone(getter_AddRefs(oldLockFile));
if (NS_FAILED(rv))
return rv;
rv = oldLockFile->Append(OLD_LOCKFILE_NAME);
if (NS_FAILED(rv))
return rv;
nsCAutoString oldFilePath;
rv = oldLockFile->GetNativePath(oldFilePath);
if (NS_FAILED(rv))
return rv;
// First, try locking using fcntl. It is more reliable on
// a local machine, but may not be supported by an NFS server.
rv = LockWithFcntl(filePath);
if (NS_SUCCEEDED(rv)) {
// Check to see whether there is a symlink lock held by an older
// Firefox build, and also place our own symlink lock --- but
// mark it "obsolete" so that other newer builds can break the lock
// if they obtain the fcntl lock
rv = LockWithSymlink(oldFilePath, PR_TRUE);
}
else if (rv != NS_ERROR_FILE_ACCESS_DENIED)
{
// If that failed for any reason other than NS_ERROR_FILE_ACCESS_DENIED,
// assume we tried an NFS that does not support it. Now, try with symlink
// using the old symlink path
rv = LockWithSymlink(oldFilePath, PR_FALSE);
}
#elif defined(XP_WIN)
nsCAutoString filePath;
rv = lockFile->GetNativePath(filePath);
@ -539,38 +594,7 @@ nsresult nsProfileLock::Lock(nsILocalFile* aProfileDir,
return NS_ERROR_FAILURE;
}
}
#elif defined(XP_UNIX)
nsCOMPtr<nsIFile> oldLockFile;
rv = aProfileDir->Clone(getter_AddRefs(oldLockFile));
if (NS_SUCCEEDED(rv))
{
rv = oldLockFile->Append(OLD_LOCKFILE_NAME);
if (NS_SUCCEEDED(rv))
{
nsCAutoString filePath;
rv = oldLockFile->GetNativePath(filePath);
if (NS_SUCCEEDED(rv))
{
// First, try the 4.x-compatible symlink technique, which works
// with NFS without depending on (broken or missing, too often)
// lockd.
rv = LockWithSymlink(filePath);
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ACCESS_DENIED)
{
// If that failed with an error other than
// NS_ERROR_FILE_ACCESS_DENIED, symlinks aren't
// supported (for example, on Win32 SAMBA servers).
// Try locking with fcntl.
rv = lockFile->GetNativePath(filePath);
if (NS_SUCCEEDED(rv))
rv = LockWithFcntl(filePath);
}
}
}
}
if (NS_FAILED(rv))
return rv;
#endif /* XP_UNIX */
#endif
mHaveLock = PR_TRUE;

View File

@ -96,7 +96,12 @@ private:
static PRCList mPidLockList;
nsresult LockWithFcntl(const nsACString& lockFilePath);
nsresult LockWithSymlink(const nsACString& lockFilePath);
/**
* @param aHaveFcntlLock if true, we've already acquired an fcntl lock so this
* lock is merely an "obsolete" lock to keep out old Firefoxes
*/
nsresult LockWithSymlink(const nsACString& lockFilePath, PRBool aHaveFcntlLock);
char* mPidLockFileName;
int mLockFileDesc;