/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ /** * File locking utilities * @file lockfile.h */ #ifndef INCLUDE__LOCK_FILE_H_ #define INCLUDE__LOCK_FILE_H_ #include #include #include #include #include #include #include #define LCK "KICAD_LOCKING" class LOCKFILE { public: LOCKFILE( const wxString &filename, bool aRemoveOnRelease = true ) : m_originalFile( filename ), m_fileCreated( false ), m_status( false ), m_removeOnRelease( aRemoveOnRelease ), m_errorMsg( "" ) { if( filename.IsEmpty() ) return; wxLogTrace( LCK, "Trying to lock %s", filename ); wxFileName fn( filename ); fn.SetName( LockFilePrefix + fn.GetName() ); fn.SetExt( fn.GetExt() + '.' + LockFileExtension ); if( !fn.IsDirWritable() ) { wxLogTrace( LCK, "File is not writable: %s", filename ); m_status = true; m_removeOnRelease = false; return; } m_lockFilename = fn.GetFullPath(); wxFile file; try { bool lock_success = false; bool rw_success = false; { wxLogNull suppressExpectedErrorMessages; lock_success = file.Open( m_lockFilename, wxFile::write_excl ); if( !lock_success ) rw_success = file.Open( m_lockFilename, wxFile::read ); } if( lock_success ) { // Lock file doesn't exist, create one m_fileCreated = true; m_status = true; m_username = wxGetUserId(); m_hostname = wxGetHostName(); nlohmann::json j; j["username"] = std::string( m_username.mb_str() ); j["hostname"] = std::string( m_hostname.mb_str() ); std::string lock_info = j.dump(); file.Write( lock_info ); file.Close(); wxLogTrace( LCK, "Locked %s", filename ); } else if( rw_success ) { // Lock file already exists, read the details wxString lock_info; file.ReadAll( &lock_info ); nlohmann::json j = nlohmann::json::parse( std::string( lock_info.mb_str() ) ); m_username = wxString( j["username"].get() ); m_hostname = wxString( j["hostname"].get() ); file.Close(); m_errorMsg = _( "Lock file already exists" ); wxLogTrace( LCK, "Existing Lock for %s", filename ); } else { throw std::runtime_error( "Failed to open lock file" ); } } catch( std::exception& e ) { wxLogError( "Got an error trying to lock %s: %s", filename, e.what() ); // Delete lock file if it was created above but we threw an exception somehow if( m_fileCreated ) { wxRemoveFile( m_lockFilename ); m_fileCreated = false; // Reset the flag since file has been deleted manually } m_errorMsg = _( "Failed to access lock file" ); m_status = false; } } ~LOCKFILE() { UnlockFile(); } /** * Unlocks and removes the file from the filesystem as long as we still own it. */ void UnlockFile() { wxLogTrace( LCK, "Unlocking %s", m_lockFilename ); // Delete lock file only if the file was created in the constructor and if the file contains the correct user and host names if( m_fileCreated && checkUserAndHost() ) { if( m_removeOnRelease ) wxRemoveFile( m_lockFilename ); m_fileCreated = false; // Reset the flag since file has been deleted manually m_status = false; m_errorMsg = wxEmptyString; } } /** * Forces the lock, overwriting the data that existed already * @return True if we successfully overrode the lock */ bool OverrideLock( bool aRemoveOnRelease = true ) { wxLogTrace( LCK, "Overriding lock on %s", m_lockFilename ); if( !m_fileCreated ) { try { wxFile file; bool success = false; { wxLogNull suppressExpectedErrorMessages; success = file.Open( m_lockFilename, wxFile::write ); } if( success ) { m_username = wxGetUserId(); m_hostname = wxGetHostName(); nlohmann::json j; j["username"] = std::string( m_username.mb_str() ); j["hostname"] = std::string( m_hostname.mb_str() ); std::string lock_info = j.dump(); file.Write( lock_info ); file.Close(); m_fileCreated = true; m_status = true; m_removeOnRelease = aRemoveOnRelease; m_errorMsg = wxEmptyString; wxLogTrace( LCK, "Successfully overrode lock on %s", m_lockFilename ); return true; } return false; } catch( std::exception& e ) { wxLogError( "Got exception trying to override lock on %s: %s", m_lockFilename, e.what() ); return false; } } else { wxLogTrace( LCK, "Upgraded lock on %s to delete on release", m_lockFilename ); m_removeOnRelease = aRemoveOnRelease; } return true; } bool IsLockedByMe() { return m_username == wxGetUserId() && m_hostname == wxGetHostName(); } /** * @return Current username. If we own the lock, this is us. Otherwise, this is the user that does own it */ wxString GetUsername(){ return m_username; } /** * @return Current hostname. If we own the lock this is our computer. Otherwise, this is the computer that does */ wxString GetHostname(){ return m_hostname; } /** * @return Last error message generated */ wxString GetErrorMsg(){ return m_errorMsg; } bool Locked() const { return m_fileCreated; } bool Valid() const { return m_status; } explicit operator bool() const { return m_status; } private: wxString m_originalFile; wxString m_lockFilename; wxString m_username; wxString m_hostname; bool m_fileCreated; bool m_status; bool m_removeOnRelease; wxString m_errorMsg; bool checkUserAndHost() { wxFileName fileName( m_lockFilename ); if( !fileName.FileExists() ) { wxLogTrace( LCK, "File does not exist: %s", m_lockFilename ); return false; } wxFile file; try { if( file.Open( m_lockFilename, wxFile::read ) ) { wxString lock_info; file.ReadAll( &lock_info ); nlohmann::json j = nlohmann::json::parse( std::string( lock_info.mb_str() ) ); if( m_username == wxString( j["username"].get() ) && m_hostname == wxString( j["hostname"].get() ) ) { wxLogTrace( LCK, "User and host match for lock %s", m_lockFilename ); return true; } } } catch( std::exception &e ) { wxLogError( "Got exception trying to check user/host for lock on %s: %s", m_lockFilename, e.what() ); } wxLogTrace( LCK, "User and host DID NOT match for lock %s", m_lockFilename ); return false; } }; #endif // INCLUDE__LOCK_FILE_H_