You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

296 lines
9.0 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. /**
  24. * File locking utilities
  25. * @file lockfile.h
  26. */
  27. #ifndef INCLUDE__LOCK_FILE_H_
  28. #define INCLUDE__LOCK_FILE_H_
  29. #include <wx/wx.h>
  30. #include <wx/file.h>
  31. #include <wx/filefn.h>
  32. #include <wx/log.h>
  33. #include <wx/filename.h>
  34. #include <nlohmann/json.hpp>
  35. #include <wildcards_and_files_ext.h>
  36. #define LCK "KICAD_LOCKING"
  37. class LOCKFILE
  38. {
  39. public:
  40. LOCKFILE( const wxString &filename, bool aRemoveOnRelease = true ) :
  41. m_originalFile( filename ), m_fileCreated( false ), m_status( false ),
  42. m_removeOnRelease( aRemoveOnRelease ), m_errorMsg( "" )
  43. {
  44. if( filename.IsEmpty() )
  45. return;
  46. wxLogTrace( LCK, "Trying to lock %s", filename );
  47. wxFileName fn( filename );
  48. fn.SetName( LockFilePrefix + fn.GetName() );
  49. fn.SetExt( fn.GetExt() + '.' + LockFileExtension );
  50. if( !fn.IsDirWritable() )
  51. {
  52. wxLogTrace( LCK, "File is not writable: %s", filename );
  53. m_status = true;
  54. m_removeOnRelease = false;
  55. return;
  56. }
  57. m_lockFilename = fn.GetFullPath();
  58. wxFile file;
  59. try
  60. {
  61. bool lock_success = false;
  62. bool rw_success = false;
  63. {
  64. wxLogNull suppressExpectedErrorMessages;
  65. lock_success = file.Open( m_lockFilename, wxFile::write_excl );
  66. if( !lock_success )
  67. rw_success = file.Open( m_lockFilename, wxFile::read );
  68. }
  69. if( lock_success )
  70. {
  71. // Lock file doesn't exist, create one
  72. m_fileCreated = true;
  73. m_status = true;
  74. m_username = wxGetUserId();
  75. m_hostname = wxGetHostName();
  76. nlohmann::json j;
  77. j["username"] = std::string( m_username.mb_str() );
  78. j["hostname"] = std::string( m_hostname.mb_str() );
  79. std::string lock_info = j.dump();
  80. file.Write( lock_info );
  81. file.Close();
  82. wxLogTrace( LCK, "Locked %s", filename );
  83. }
  84. else if( rw_success )
  85. {
  86. // Lock file already exists, read the details
  87. wxString lock_info;
  88. file.ReadAll( &lock_info );
  89. nlohmann::json j = nlohmann::json::parse( std::string( lock_info.mb_str() ) );
  90. m_username = wxString( j["username"].get<std::string>() );
  91. m_hostname = wxString( j["hostname"].get<std::string>() );
  92. file.Close();
  93. m_errorMsg = _( "Lock file already exists" );
  94. wxLogTrace( LCK, "Existing Lock for %s", filename );
  95. }
  96. else
  97. {
  98. throw std::runtime_error( "Failed to open lock file" );
  99. }
  100. }
  101. catch( std::exception& e )
  102. {
  103. wxLogError( "Got an error trying to lock %s: %s", filename, e.what() );
  104. // Delete lock file if it was created above but we threw an exception somehow
  105. if( m_fileCreated )
  106. {
  107. wxRemoveFile( m_lockFilename );
  108. m_fileCreated = false; // Reset the flag since file has been deleted manually
  109. }
  110. m_errorMsg = _( "Failed to access lock file" );
  111. m_status = false;
  112. }
  113. }
  114. ~LOCKFILE()
  115. {
  116. UnlockFile();
  117. }
  118. /**
  119. * Unlocks and removes the file from the filesystem as long as we still own it.
  120. */
  121. void UnlockFile()
  122. {
  123. wxLogTrace( LCK, "Unlocking %s", m_lockFilename );
  124. // Delete lock file only if the file was created in the constructor and if the file contains the correct user and host names
  125. if( m_fileCreated && checkUserAndHost() )
  126. {
  127. if( m_removeOnRelease )
  128. wxRemoveFile( m_lockFilename );
  129. m_fileCreated = false; // Reset the flag since file has been deleted manually
  130. m_status = false;
  131. m_errorMsg = wxEmptyString;
  132. }
  133. }
  134. /**
  135. * Forces the lock, overwriting the data that existed already
  136. * @return True if we successfully overrode the lock
  137. */
  138. bool OverrideLock( bool aRemoveOnRelease = true )
  139. {
  140. wxLogTrace( LCK, "Overriding lock on %s", m_lockFilename );
  141. if( !m_fileCreated )
  142. {
  143. try
  144. {
  145. wxFile file;
  146. bool success = false;
  147. {
  148. wxLogNull suppressExpectedErrorMessages;
  149. success = file.Open( m_lockFilename, wxFile::write );
  150. }
  151. if( success )
  152. {
  153. m_username = wxGetUserId();
  154. m_hostname = wxGetHostName();
  155. nlohmann::json j;
  156. j["username"] = std::string( m_username.mb_str() );
  157. j["hostname"] = std::string( m_hostname.mb_str() );
  158. std::string lock_info = j.dump();
  159. file.Write( lock_info );
  160. file.Close();
  161. m_fileCreated = true;
  162. m_status = true;
  163. m_removeOnRelease = aRemoveOnRelease;
  164. m_errorMsg = wxEmptyString;
  165. wxLogTrace( LCK, "Successfully overrode lock on %s", m_lockFilename );
  166. return true;
  167. }
  168. return false;
  169. }
  170. catch( std::exception& e )
  171. {
  172. wxLogError( "Got exception trying to override lock on %s: %s",
  173. m_lockFilename, e.what() );
  174. return false;
  175. }
  176. }
  177. else
  178. {
  179. wxLogTrace( LCK, "Upgraded lock on %s to delete on release", m_lockFilename );
  180. m_removeOnRelease = aRemoveOnRelease;
  181. }
  182. return true;
  183. }
  184. bool IsLockedByMe()
  185. {
  186. return m_username == wxGetUserId() && m_hostname == wxGetHostName();
  187. }
  188. /**
  189. * @return Current username. If we own the lock, this is us. Otherwise, this is the user that does own it
  190. */
  191. wxString GetUsername(){ return m_username; }
  192. /**
  193. * @return Current hostname. If we own the lock this is our computer. Otherwise, this is the computer that does
  194. */
  195. wxString GetHostname(){ return m_hostname; }
  196. /**
  197. * @return Last error message generated
  198. */
  199. wxString GetErrorMsg(){ return m_errorMsg; }
  200. bool Locked() const
  201. {
  202. return m_fileCreated;
  203. }
  204. bool Valid() const
  205. {
  206. return m_status;
  207. }
  208. explicit operator bool() const
  209. {
  210. return m_status;
  211. }
  212. private:
  213. wxString m_originalFile;
  214. wxString m_lockFilename;
  215. wxString m_username;
  216. wxString m_hostname;
  217. bool m_fileCreated;
  218. bool m_status;
  219. bool m_removeOnRelease;
  220. wxString m_errorMsg;
  221. bool checkUserAndHost()
  222. {
  223. wxFileName fileName( m_lockFilename );
  224. if( !fileName.FileExists() )
  225. {
  226. wxLogTrace( LCK, "File does not exist: %s", m_lockFilename );
  227. return false;
  228. }
  229. wxFile file;
  230. try
  231. {
  232. if( file.Open( m_lockFilename, wxFile::read ) )
  233. {
  234. wxString lock_info;
  235. file.ReadAll( &lock_info );
  236. nlohmann::json j = nlohmann::json::parse( std::string( lock_info.mb_str() ) );
  237. if( m_username == wxString( j["username"].get<std::string>() )
  238. && m_hostname == wxString( j["hostname"].get<std::string>() ) )
  239. {
  240. wxLogTrace( LCK, "User and host match for lock %s", m_lockFilename );
  241. return true;
  242. }
  243. }
  244. }
  245. catch( std::exception &e )
  246. {
  247. wxLogError( "Got exception trying to check user/host for lock on %s: %s",
  248. m_lockFilename,
  249. e.what() );
  250. }
  251. wxLogTrace( LCK, "User and host DID NOT match for lock %s", m_lockFilename );
  252. return false;
  253. }
  254. };
  255. #endif // INCLUDE__LOCK_FILE_H_