7fe83993cf
If KiCad crashes or exits without deleting the lockfile, don't show the warning message unless we are not the one who locked it or there are other KiCad instances running locally. This should catch 99% of the cases where the message is shown incorrectly. There may be some corner cases where the lock file is created on a network drive using two different machines with the same name and same user but these cases should be (famous last words) sufficiently rare as to not be observed in practice
297 lines
9.0 KiB
C++
297 lines
9.0 KiB
C++
/*
|
|
* 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 <wx/wx.h>
|
|
#include <wx/file.h>
|
|
#include <wx/filefn.h>
|
|
#include <wx/log.h>
|
|
#include <wx/filename.h>
|
|
#include <nlohmann/json.hpp>
|
|
#include <wildcards_and_files_ext.h>
|
|
|
|
#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<std::string>() );
|
|
m_hostname = wxString( j["hostname"].get<std::string>() );
|
|
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<std::string>() )
|
|
&& m_hostname == wxString( j["hostname"].get<std::string>() ) )
|
|
{
|
|
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_
|