Prevent multiple instances from running on Unix systems.

Every call to RsInit::LoadCertificates() now creates a file:
~/.retroshare/xxxxxxxxxxxxxxxxxxxx/lock
which is then bound to a system lock (fcntl F_SETLK).

If the lock request fails, it means another instance is already
running with the same profile.


git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@3241 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
leander-256 2010-07-01 20:30:36 +00:00
parent 840f077826
commit fe46d7618a
7 changed files with 221 additions and 47 deletions

View File

@ -310,7 +310,7 @@ int AuthGPG::GPGInit(std::string ownId)
std::cerr << "AuthGPG::GPGInit finished." << std::endl; std::cerr << "AuthGPG::GPGInit finished." << std::endl;
return true; return 1;
} }
AuthGPG::~AuthGPG() AuthGPG::~AuthGPG()
@ -780,7 +780,7 @@ bool AuthGPG::DoOwnSignature(const void *data, unsigned int datalen, void *buf_s
// gpgme_data_write (gpgmeSig, "", 1); // to be able to convert it into a string // gpgme_data_write (gpgmeSig, "", 1); // to be able to convert it into a string
char *export_sig = gpgme_data_release_and_get_mem(gpgmeSig, &len); char *export_sig = gpgme_data_release_and_get_mem(gpgmeSig, &len);
#ifdef GPG_DEBUG #ifdef GPG_DEBUG
fprintf(stderr, "AuthGPG::Signature len: %d \n", len); std::cerr << "AuthGPG::Signature len: " << len << std::endl;
#endif #endif
if (len < *outl) // -1 because we added a 0 at the end. if (len < *outl) // -1 because we added a 0 at the end.

View File

@ -66,7 +66,7 @@ class RsInit
static bool GeneratePGPCertificate(std::string name, std::string email, std::string passwd, std::string &pgpId, std::string &errString); static bool GeneratePGPCertificate(std::string name, std::string email, std::string passwd, std::string &pgpId, std::string &errString);
/* Login PGP */ /* Login PGP */
static bool SelectGPGAccount(std::string id); static bool SelectGPGAccount(const std::string& gpgId);
static bool LoadGPGPassword(std::string passwd); static bool LoadGPGPassword(std::string passwd);
/* Create SSL Certificates */ /* Create SSL Certificates */
@ -78,8 +78,11 @@ class RsInit
/** Final Certificate load. This can be called if: /** Final Certificate load. This can be called if:
* a) InitRetroshare() returns true -> autoLoad/password Set. * a) InitRetroshare() returns true -> autoLoad/password Set.
* b) SelectGPGAccount() && LoadPassword() * b) SelectGPGAccount() && LoadPassword()
*
* This wrapper is used to lock the profile first before
* finalising the login
*/ */
static int LoadCertificates(bool autoLoginNT) ; static int LockAndLoadCertificates(bool autoLoginNT);
/* Post Login Options */ /* Post Login Options */
@ -108,6 +111,12 @@ class RsInit
static bool RsStoreAutoLogin() ; static bool RsStoreAutoLogin() ;
static bool RsTryAutoLogin() ; static bool RsTryAutoLogin() ;
/* Lock/unlock profile directory */
static int LockConfigDirectory(const std::string& accountDir);
static void UnlockConfigDirectory();
/* The true LoadCertificates() method */
static int LoadCertificates(bool autoLoginNT) ;
}; };

View File

@ -27,6 +27,12 @@
* the GUI / External via a hidden class */ * the GUI / External via a hidden class */
#include <unistd.h> #include <unistd.h>
// for locking instances
#ifndef WINDOWS_SYS
#include <errno.h>
#endif
#include "util/rsdebug.h" #include "util/rsdebug.h"
#include "util/rsdir.h" #include "util/rsdir.h"
#include "rsiface/rsinit.h" #include "rsiface/rsinit.h"
@ -78,6 +84,11 @@ class RsInitConfig
/* for certificate creation */ /* for certificate creation */
//static std::string gpgPasswd; //static std::string gpgPasswd;
#ifndef WINDOWS_SYS
static int lockHandle;
#else
#endif
/* These fields are needed for login */ /* These fields are needed for login */
static std::string loginId; static std::string loginId;
static std::string configDir; static std::string configDir;
@ -131,6 +142,11 @@ static const int SSLPWD_LEN = 6;
std::list<accountId> RsInitConfig::accountIds; std::list<accountId> RsInitConfig::accountIds;
std::string RsInitConfig::preferedId; std::string RsInitConfig::preferedId;
#ifndef WINDOWS_SYS
int RsInitConfig::lockHandle;
#else
#endif
std::string RsInitConfig::configDir; std::string RsInitConfig::configDir;
std::string RsInitConfig::load_cert; std::string RsInitConfig::load_cert;
std::string RsInitConfig::load_key; std::string RsInitConfig::load_key;
@ -203,6 +219,7 @@ void RsInit::InitRsConfig()
{ {
#ifndef WINDOWS_SYS #ifndef WINDOWS_SYS
RsInitConfig::dirSeperator = '/'; // For unix. RsInitConfig::dirSeperator = '/'; // For unix.
RsInitConfig::lockHandle = -1;
#else #else
RsInitConfig::dirSeperator = '\\'; // For windows. RsInitConfig::dirSeperator = '\\'; // For windows.
#endif #endif
@ -878,29 +895,102 @@ int RsInit::GetPGPLoginDetails(std::string id, std::string &name, std::stri
} }
} }
/*
* To prevent several running instances from using the same directory
* simultaneously we have to use a global lock.
* We use a lock file on Unix systems.
*
* Return value:
* 0 : Success
* 1 : Another instance already has the lock
* 2 : Unexpected error
*/
int RsInit::LockConfigDirectory(const std::string& accountDir)
{
/******************************** WINDOWS/UNIX SPECIFIC PART ******************/
#ifndef WINDOWS_SYS
const std::string lockFile = accountDir + RsInitConfig::dirSeperator + "lock";
if(RsInitConfig::lockHandle != -1)
close(RsInitConfig::lockHandle);
// open the file in write mode, create it if necessary, truncate it (it should be empty)
RsInitConfig::lockHandle = open(lockFile.c_str(), O_WRONLY | O_CREAT | O_TRUNC);
if(RsInitConfig::lockHandle == -1)
{
std::cerr << "Could not open lock file " << lockFile.c_str() << std::flush;
perror(NULL);
return 2;
}
// see "man fcntl" for the details, in short: non blocking lock creation on the whole file contents
struct flock lockDetails;
lockDetails.l_type = F_WRLCK;
lockDetails.l_whence = SEEK_SET;
lockDetails.l_start = 0;
lockDetails.l_len = 0;
if(fcntl(RsInitConfig::lockHandle, F_SETLK, &lockDetails) == -1)
{
int fcntlErr = errno;
std::cerr << "Could not request lock on file " << lockFile.c_str() << std::flush;
perror(NULL);
// there's no lock so let's release the file handle immediately
close(RsInitConfig::lockHandle);
RsInitConfig::lockHandle = -1;
if(fcntlErr == EACCES || fcntlErr == EAGAIN)
return 1;
else
return 2;
}
return 0;
#else
return 0;
#endif
/******************************** WINDOWS/UNIX SPECIFIC PART ******************/
}
/*
* Unlock the currently locked profile, if there is one.
* For Unix systems we simply close the handle of the lock file.
*/
void RsInit::UnlockConfigDirectory()
{
/******************************** WINDOWS/UNIX SPECIFIC PART ******************/
#ifndef WINDOWS_SYS
if(RsInitConfig::lockHandle != -1)
{
close(RsInitConfig::lockHandle);
RsInitConfig::lockHandle = -1;
}
#else
#endif
/******************************** WINDOWS/UNIX SPECIFIC PART ******************/
}
/* Before any SSL stuff can be loaded, the correct PGP must be selected / generated: /* Before any SSL stuff can be loaded, the correct PGP must be selected / generated:
**/ **/
bool RsInit::SelectGPGAccount(std::string id) bool RsInit::SelectGPGAccount(const std::string& gpgId)
{ {
bool ok = false; bool retVal = false;
std::string gpgId = id;
std::string name = id;
if (0 < AuthGPG::getAuthGPG() -> GPGInit(gpgId)) if (0 < AuthGPG::getAuthGPG() -> GPGInit(gpgId))
{ {
ok = true; retVal = true;
std::cerr << "PGP Auth Success! "; std::cerr << "PGP Auth Success!";
std::cerr << "ID: " << id << " NAME: " << name;
std::cerr << std::endl;
} }
else else
{
std::cerr << "PGP Auth Failed!"; std::cerr << "PGP Auth Failed!";
std::cerr << "ID: " << id << " NAME: " << name;
std::cerr << std::endl; std::cerr << " ID: " << gpgId << std::endl;
}
return ok; return retVal;
} }
@ -1044,7 +1134,6 @@ bool RsInit::GenerateSSLCertificate(std::string gpg_id, std::string org, std
std::cerr << "rename FAILED" << std::endl; std::cerr << "rename FAILED" << std::endl;
} }
/* Flag as first time run */ /* Flag as first time run */
RsInitConfig::firsttime_run = true; RsInitConfig::firsttime_run = true;
@ -1145,13 +1234,40 @@ bool RsInit::LoadPassword(std::string id, std::string inPwd)
} }
/**
* Locks the profile directory and tries to finalize the login procedure
*
* Return value:
* 0 : success
* 1 : another instance is already running
* 2 : unexpected error while locking
* 3 : unexpected error while loading certificates
*/
int RsInit::LockAndLoadCertificates(bool autoLoginNT)
{
int retVal = LockConfigDirectory(RsInitConfig::configDir);
if(retVal != 0)
return retVal;
/***************************** FINAL LOADING OF SETUP ************************* retVal = LoadCertificates(autoLoginNT);
if(retVal != 1) {
UnlockConfigDirectory();
return 3;
}
return 0;
}
/** *************************** FINAL LOADING OF SETUP *************************
* Requires: * Requires:
* PGPid to be selected (Password not required). * PGPid to be selected (Password not required).
* CertId to be selected (Password Required). * CertId to be selected (Password Required).
*
* Return value:
* 0 : unexpected error
* 1 : success
*/ */
int RsInit::LoadCertificates(bool autoLoginNT) int RsInit::LoadCertificates(bool autoLoginNT)
{ {
@ -1224,7 +1340,7 @@ int RsInit::LoadCertificates(bool autoLoginNT)
FILE *sslPassphraseFile = fopen(RsInitConfig::ssl_passphrase_file.c_str(), "r"); FILE *sslPassphraseFile = fopen(RsInitConfig::ssl_passphrase_file.c_str(), "r");
if (sslPassphraseFile == NULL) if (sslPassphraseFile == NULL)
{ {
std::cerr << "No password povided, and no sslPassphraseFile : " << RsInitConfig::ssl_passphrase_file.c_str() << std::endl; std::cerr << "No password provided, and no sslPassphraseFile : " << RsInitConfig::ssl_passphrase_file.c_str() << std::endl;
return 0; return 0;
} else { } else {
std::cerr << "opening sslPassphraseFile : " << RsInitConfig::ssl_passphrase_file.c_str() << std::endl; std::cerr << "opening sslPassphraseFile : " << RsInitConfig::ssl_passphrase_file.c_str() << std::endl;

View File

@ -320,19 +320,27 @@ void GenCertDialog::checkChanged(int i)
void GenCertDialog::loadCertificates() void GenCertDialog::loadCertificates()
{ {
bool autoSave = false; int retVal = RsInit::LockAndLoadCertificates(false);
/* Final stage of loading */ switch(retVal)
if (RsInit::LoadCertificates(autoSave))
{ {
close(); case 0: close();
} break;
else case 1: QMessageBox::warning( this,
{ tr("Multiple instances"),
/* some error msg */ tr("Another RetroShare using the same profile is "
QMessageBox::warning ( NULL, "already running on your system. Please close "
"Generate ID Failure", "that instance first") );
"Failed to Load your new Certificate!", break;
QMessageBox::Ok); case 2: QMessageBox::warning( this,
tr("Multiple instances"),
tr("An unexpected error occurred when Retroshare"
"tried to acquire the single instance lock") );
break;
case 3: QMessageBox::warning( this,
tr("Generate ID Failure"),
tr("Failed to Load your new Certificate!") );
break;
default: std::cerr << "StartDialog::loadCertificates() unexpected switch value " << retVal << std::endl;
} }
} }

View File

@ -152,18 +152,27 @@ void StartDialog::loadPerson()
void StartDialog::loadCertificates() void StartDialog::loadCertificates()
{ {
/* Final stage of loading */ /* Final stage of loading */
int retVal = RsInit::LockAndLoadCertificates(ui.autologin_checkbox->isChecked());
if (RsInit::LoadCertificates(ui.autologin_checkbox->isChecked())) switch(retVal)
{ {
close(); case 0: close();
} break;
else case 1: QMessageBox::warning( this,
{ tr("Multiple instances"),
/* some error msg */ tr("Another RetroShare using the same profile is "
QMessageBox::warning ( this, "already running on your system. Please close "
tr("Login Failure"), "that instance first, or choose another profile") );
tr("Maybe password is wrong"), break;
QMessageBox::Ok); case 2: QMessageBox::warning( this,
tr("Multiple instances"),
tr("An unexpected error occurred when Retroshare"
"tried to acquire the single instance lock") );
break;
case 3: QMessageBox::warning( this,
tr("Login Failure"),
tr("Maybe password is wrong") );
break;
default: std::cerr << "StartDialog::loadCertificates() unexpected switch value " << retVal << std::endl;
} }
} }

View File

@ -129,7 +129,27 @@ int main(int argc, char *argv[])
} }
// true: note auto-login is active // true: note auto-login is active
RsInit::LoadCertificates(true); int retVal = RsInit::LockAndLoadCertificates(true);
switch(retVal)
{
case 0: break;
case 1: QMessageBox::warning( 0,
QObject::tr("Multiple instances"),
QObject::tr("Another RetroShare using the same profile is "
"already running on your system. Please close "
"that instance first") );
return 1;
case 2: QMessageBox::critical( 0,
QObject::tr("Multiple instances"),
QObject::tr("An unexpected error occurred when Retroshare"
"tried to acquire the single instance lock") );
return 1;
case 3: QMessageBox::critical( 0,
QObject::tr("Login Failure"),
QObject::tr("Maybe password is wrong") );
return 1;
default: std::cerr << "StartDialog::loadCertificates() unexpected switch value " << retVal << std::endl;
}
} }
rsicontrol->StartupRetroShare(); rsicontrol->StartupRetroShare();

View File

@ -91,7 +91,19 @@ int main(int argc, char **argv)
/* Key + Certificate are loaded into libretroshare */ /* Key + Certificate are loaded into libretroshare */
RsInit::LoadCertificates(false); int retVal = RsInit::LockAndLoadCertificates(false);
switch(retVal)
{
case 0: break;
case 1: std::cerr << "Error: another instance of retroshare is already using this profile" << std::endl;
return 1;
case 2: std::cerr << "An unexpected error occurred while locking the profile" << std::endl;
return 1;
case 3: std::cerr << "An error occurred while login with the profile" << std::endl;
return 1;
default: std::cerr << "Main: Unexpected switch value " << retVal << std::endl;
return 1;
}
/* Start-up libretroshare server threads */ /* Start-up libretroshare server threads */
rsServer -> StartupRetroShare(); rsServer -> StartupRetroShare();