diff --git a/libretroshare/src/pqi/authgpg.cc b/libretroshare/src/pqi/authgpg.cc index 755f71ba1..5180f32a3 100644 --- a/libretroshare/src/pqi/authgpg.cc +++ b/libretroshare/src/pqi/authgpg.cc @@ -310,7 +310,7 @@ int AuthGPG::GPGInit(std::string ownId) std::cerr << "AuthGPG::GPGInit finished." << std::endl; - return true; + return 1; } 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 char *export_sig = gpgme_data_release_and_get_mem(gpgmeSig, &len); #ifdef GPG_DEBUG - fprintf(stderr, "AuthGPG::Signature len: %d \n", len); + std::cerr << "AuthGPG::Signature len: " << len << std::endl; #endif if (len < *outl) // -1 because we added a 0 at the end. diff --git a/libretroshare/src/rsiface/rsinit.h b/libretroshare/src/rsiface/rsinit.h index bd907fa63..43e4caba1 100644 --- a/libretroshare/src/rsiface/rsinit.h +++ b/libretroshare/src/rsiface/rsinit.h @@ -66,7 +66,7 @@ class RsInit static bool GeneratePGPCertificate(std::string name, std::string email, std::string passwd, std::string &pgpId, std::string &errString); /* Login PGP */ - static bool SelectGPGAccount(std::string id); + static bool SelectGPGAccount(const std::string& gpgId); static bool LoadGPGPassword(std::string passwd); /* Create SSL Certificates */ @@ -78,8 +78,11 @@ class RsInit /** Final Certificate load. This can be called if: * a) InitRetroshare() returns true -> autoLoad/password Set. * 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 */ @@ -108,6 +111,12 @@ class RsInit static bool RsStoreAutoLogin() ; 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) ; }; diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 5ca90fabb..a3dc6a71c 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -27,6 +27,12 @@ * the GUI / External via a hidden class */ #include + +// for locking instances +#ifndef WINDOWS_SYS +#include +#endif + #include "util/rsdebug.h" #include "util/rsdir.h" #include "rsiface/rsinit.h" @@ -78,6 +84,11 @@ class RsInitConfig /* for certificate creation */ //static std::string gpgPasswd; +#ifndef WINDOWS_SYS + static int lockHandle; +#else +#endif + /* These fields are needed for login */ static std::string loginId; static std::string configDir; @@ -131,6 +142,11 @@ static const int SSLPWD_LEN = 6; std::list RsInitConfig::accountIds; std::string RsInitConfig::preferedId; +#ifndef WINDOWS_SYS + int RsInitConfig::lockHandle; +#else +#endif + std::string RsInitConfig::configDir; std::string RsInitConfig::load_cert; std::string RsInitConfig::load_key; @@ -203,6 +219,7 @@ void RsInit::InitRsConfig() { #ifndef WINDOWS_SYS RsInitConfig::dirSeperator = '/'; // For unix. + RsInitConfig::lockHandle = -1; #else RsInitConfig::dirSeperator = '\\'; // For windows. #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: **/ -bool RsInit::SelectGPGAccount(std::string id) +bool RsInit::SelectGPGAccount(const std::string& gpgId) { - bool ok = false; - std::string gpgId = id; - std::string name = id; + bool retVal = false; - if (0 < AuthGPG::getAuthGPG() -> GPGInit(gpgId)) + if (0 < AuthGPG::getAuthGPG() -> GPGInit(gpgId)) { - ok = true; - std::cerr << "PGP Auth Success! "; - std::cerr << "ID: " << id << " NAME: " << name; - std::cerr << std::endl; + retVal = true; + std::cerr << "PGP Auth Success!"; } else - { std::cerr << "PGP Auth Failed!"; - std::cerr << "ID: " << id << " NAME: " << name; - std::cerr << std::endl; - } - return ok; + + std::cerr << " ID: " << gpgId << std::endl; + + return retVal; } @@ -1044,7 +1134,6 @@ bool RsInit::GenerateSSLCertificate(std::string gpg_id, std::string org, std std::cerr << "rename FAILED" << std::endl; } - /* Flag as first time run */ 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: * PGPid to be selected (Password not required). * CertId to be selected (Password Required). + * + * Return value: + * 0 : unexpected error + * 1 : success */ - int RsInit::LoadCertificates(bool autoLoginNT) { @@ -1224,7 +1340,7 @@ int RsInit::LoadCertificates(bool autoLoginNT) FILE *sslPassphraseFile = fopen(RsInitConfig::ssl_passphrase_file.c_str(), "r"); 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; } else { std::cerr << "opening sslPassphraseFile : " << RsInitConfig::ssl_passphrase_file.c_str() << std::endl; diff --git a/retroshare-gui/src/gui/GenCertDialog.cpp b/retroshare-gui/src/gui/GenCertDialog.cpp index 0154cf21f..076ed9f74 100644 --- a/retroshare-gui/src/gui/GenCertDialog.cpp +++ b/retroshare-gui/src/gui/GenCertDialog.cpp @@ -320,19 +320,27 @@ void GenCertDialog::checkChanged(int i) void GenCertDialog::loadCertificates() { - bool autoSave = false; - /* Final stage of loading */ - if (RsInit::LoadCertificates(autoSave)) + int retVal = RsInit::LockAndLoadCertificates(false); + switch(retVal) { - close(); - } - else - { - /* some error msg */ - QMessageBox::warning ( NULL, - "Generate ID Failure", - "Failed to Load your new Certificate!", - QMessageBox::Ok); + case 0: close(); + break; + case 1: QMessageBox::warning( this, + tr("Multiple instances"), + tr("Another RetroShare using the same profile is " + "already running on your system. Please close " + "that instance first") ); + break; + 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; } } diff --git a/retroshare-gui/src/gui/StartDialog.cpp b/retroshare-gui/src/gui/StartDialog.cpp index f84b4dfc6..c1f66de8a 100644 --- a/retroshare-gui/src/gui/StartDialog.cpp +++ b/retroshare-gui/src/gui/StartDialog.cpp @@ -152,18 +152,27 @@ void StartDialog::loadPerson() void StartDialog::loadCertificates() { /* Final stage of loading */ - - if (RsInit::LoadCertificates(ui.autologin_checkbox->isChecked())) + int retVal = RsInit::LockAndLoadCertificates(ui.autologin_checkbox->isChecked()); + switch(retVal) { - close(); - } - else - { - /* some error msg */ - QMessageBox::warning ( this, - tr("Login Failure"), - tr("Maybe password is wrong"), - QMessageBox::Ok); + case 0: close(); + break; + case 1: QMessageBox::warning( this, + tr("Multiple instances"), + tr("Another RetroShare using the same profile is " + "already running on your system. Please close " + "that instance first, or choose another profile") ); + break; + 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; } } diff --git a/retroshare-gui/src/main.cpp b/retroshare-gui/src/main.cpp index f09361963..6cecfacd0 100644 --- a/retroshare-gui/src/main.cpp +++ b/retroshare-gui/src/main.cpp @@ -129,7 +129,27 @@ int main(int argc, char *argv[]) } // 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(); diff --git a/retroshare-nogui/src/retroshare.cc b/retroshare-nogui/src/retroshare.cc index 481b7a0a0..3dee7efc6 100644 --- a/retroshare-nogui/src/retroshare.cc +++ b/retroshare-nogui/src/retroshare.cc @@ -91,7 +91,19 @@ int main(int argc, char **argv) /* 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 */ rsServer -> StartupRetroShare();