KiCad PCB EDA Suite
Loading...
Searching...
No Matches
lockfile.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The 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
28
29#ifndef INCLUDE__LOCK_FILE_H_
30#define INCLUDE__LOCK_FILE_H_
31
32#include <wx/wx.h>
33#include <wx/file.h>
34#include <wx/filefn.h>
35#include <wx/log.h>
36#include <wx/filename.h>
37#include <json_common.h>
39
40#define LCK "KICAD_LOCKING"
41
43{
44public:
45 LOCKFILE( const wxString &filename, bool aRemoveOnRelease = true ) :
46 m_originalFile( filename ), m_fileCreated( false ), m_status( false ),
47 m_removeOnRelease( aRemoveOnRelease ), m_errorMsg( "" )
48 {
49 if( filename.IsEmpty() )
50 return;
51
52 wxLogTrace( LCK, "Trying to lock %s", filename );
53 wxFileName fn( filename );
54 fn.SetName( FILEEXT::LockFilePrefix + fn.GetName() );
55 fn.SetExt( fn.GetExt() + '.' + FILEEXT::LockFileExtension );
56
57 if( !fn.IsDirWritable() )
58 {
59 wxLogTrace( LCK, "File is not writable: %s", filename );
60 m_status = true;
61 m_removeOnRelease = false;
62 return;
63 }
64
65 m_lockFilename = fn.GetFullPath();
66
67 wxFile file;
68 try
69 {
70 bool lock_success = false;
71 bool rw_success = false;
72
73 {
74 wxLogNull suppressExpectedErrorMessages;
75
76 lock_success = file.Open( m_lockFilename, wxFile::write_excl );
77
78 if( !lock_success )
79 rw_success = file.Open( m_lockFilename, wxFile::read );
80 }
81
82 if( lock_success )
83 {
84 m_fileCreated = true;
85 m_status = true;
86 m_username = wxGetUserId();
87 m_hostname = wxGetHostName();
88 nlohmann::json j;
89 j["username"] = std::string( m_username.mb_str() );
90 j["hostname"] = std::string( m_hostname.mb_str() );
91 std::string lock_info = j.dump();
92 file.Write( lock_info );
93 file.Close();
94 wxLogTrace( LCK, "Locked %s", filename );
95 }
96 else if( rw_success )
97 {
98 wxString lock_info;
99 file.ReadAll( &lock_info );
100 file.Close();
101
102 // Cloud-synced drives can present a lock file before its contents finish syncing.
103 // Treat an empty or corrupt file as unknown-owner stale rather than a hard error.
104 try
105 {
106 nlohmann::json j = nlohmann::json::parse( std::string( lock_info.mb_str() ) );
107 m_username = wxString( j.at( "username" ).get<std::string>() );
108 m_hostname = wxString( j.at( "hostname" ).get<std::string>() );
109 }
110 catch( const std::exception& parseError )
111 {
112 m_username = wxEmptyString;
113 m_hostname = wxEmptyString;
114 wxLogTrace( LCK, "Unreadable lock contents for %s: %s", filename,
115 parseError.what() );
116 }
117
118 m_errorMsg = _( "Lock file already exists" );
119 wxLogTrace( LCK, "Existing Lock for %s", filename );
120 }
121 else
122 {
123 throw std::runtime_error( "Failed to open lock file" );
124 }
125 }
126 catch( std::exception& e )
127 {
128 wxLogError( "Got an error trying to lock %s: %s", filename, e.what() );
129
130 if( m_fileCreated )
131 {
132 wxRemoveFile( m_lockFilename );
133 m_fileCreated = false;
134 }
135
136 m_errorMsg = _( "Failed to access lock file" );
137 m_status = false;
138 }
139 }
140
141 LOCKFILE( LOCKFILE&& other ) noexcept :
142 m_originalFile( std::move( other.m_originalFile ) ),
143 m_lockFilename( std::move( other.m_lockFilename ) ),
144 m_username( std::move( other.m_username ) ),
145 m_hostname( std::move( other.m_hostname ) ),
146 m_fileCreated( other.m_fileCreated ),
147 m_status( other.m_status ),
148 m_removeOnRelease( other.m_removeOnRelease ),
149 m_errorMsg( std::move( other.m_errorMsg ) )
150 {
151 // Disable unlock in the moved-from object
152 other.m_fileCreated = false;
153 }
154
156 {
157 UnlockFile();
158 }
159
164 {
165 wxLogTrace( LCK, "Unlocking %s", m_lockFilename );
166
168 {
170 wxRemoveFile( m_lockFilename );
171
172 m_fileCreated = false;
173 m_status = false;
174 m_errorMsg = wxEmptyString;
175 }
176 }
177
183 bool OverrideLock( bool aRemoveOnRelease = true )
184 {
185 wxLogTrace( LCK, "Overriding lock on %s", m_lockFilename );
186
187 if( !m_fileCreated )
188 {
189 try
190 {
191 wxFile file;
192 bool success = false;
193
194 {
195 wxLogNull suppressExpectedErrorMessages;
196 success = file.Open( m_lockFilename, wxFile::write );
197 }
198
199 if( success )
200 {
201 m_username = wxGetUserId();
202 m_hostname = wxGetHostName();
203 nlohmann::json j;
204 j["username"] = std::string( m_username.mb_str() );
205 j["hostname"] = std::string( m_hostname.mb_str() );
206 std::string lock_info = j.dump();
207 file.Write( lock_info );
208 file.Close();
209 m_fileCreated = true;
210 m_status = true;
211 m_removeOnRelease = aRemoveOnRelease;
212 m_errorMsg = wxEmptyString;
213 wxLogTrace( LCK, "Successfully overrode lock on %s", m_lockFilename );
214 return true;
215 }
216
217 return false;
218 }
219 catch( std::exception& e )
220 {
221 wxLogError( "Got exception trying to override lock on %s: %s",
222 m_lockFilename, e.what() );
223
224 return false;
225 }
226 }
227 else
228 {
229 wxLogTrace( LCK, "Upgraded lock on %s to delete on release", m_lockFilename );
230 m_removeOnRelease = aRemoveOnRelease;
231 }
232
233 return true;
234 }
235
237 {
238 // Empty owner means the lock file could not be parsed (e.g. partial cloud sync).
239 // We cannot prove another user owns it, so treat it as reclaimable.
240 if( m_username.IsEmpty() && m_hostname.IsEmpty() )
241 return true;
242
243 return m_username == wxGetUserId() && m_hostname == wxGetHostName();
244 }
245
250 wxString GetUsername(){ return m_username; }
251
256 wxString GetHostname(){ return m_hostname; }
257
261 wxString GetErrorMsg(){ return m_errorMsg; }
262
263 bool Locked() const
264 {
265 return m_fileCreated;
266 }
267
268 bool Valid() const
269 {
270 return m_status;
271 }
272
273 explicit operator bool() const
274 {
275 return m_status;
276 }
277
278private:
281 wxString m_username;
282 wxString m_hostname;
286 wxString m_errorMsg;
287
289 {
290 wxFileName fileName( m_lockFilename );
291
292 if( !fileName.FileExists() )
293 {
294 wxLogTrace( LCK, "File does not exist: %s", m_lockFilename );
295 return false;
296 }
297
298 wxFile file;
299
300 try
301 {
302 if( file.Open( m_lockFilename, wxFile::read ) )
303 {
304 wxString lock_info;
305 file.ReadAll( &lock_info );
306 nlohmann::json j = nlohmann::json::parse( std::string( lock_info.mb_str() ) );
307
308 if( m_username == wxString( j["username"].get<std::string>() )
309 && m_hostname == wxString( j["hostname"].get<std::string>() ) )
310 {
311 wxLogTrace( LCK, "User and host match for lock %s", m_lockFilename );
312 return true;
313 }
314 }
315 }
316 catch( std::exception &e )
317 {
318 wxLogError( "Got exception trying to check user/host for lock on %s: %s",
320 e.what() );
321 }
322
323 wxLogTrace( LCK, "User and host DID NOT match for lock %s", m_lockFilename );
324 return false;
325 }
326};
327
328
329#endif // INCLUDE__LOCK_FILE_H_
bool m_removeOnRelease
Definition lockfile.h:285
LOCKFILE(const wxString &filename, bool aRemoveOnRelease=true)
Definition lockfile.h:45
bool m_fileCreated
Definition lockfile.h:283
LOCKFILE(LOCKFILE &&other) noexcept
Definition lockfile.h:141
wxString m_errorMsg
Definition lockfile.h:286
bool m_status
Definition lockfile.h:284
wxString m_lockFilename
Definition lockfile.h:280
bool OverrideLock(bool aRemoveOnRelease=true)
Force the lock, overwriting the data that existed already.
Definition lockfile.h:183
bool Valid() const
Definition lockfile.h:268
~LOCKFILE()
Definition lockfile.h:155
wxString m_originalFile
Definition lockfile.h:279
wxString m_hostname
Definition lockfile.h:282
wxString GetUsername()
Definition lockfile.h:250
wxString GetErrorMsg()
Definition lockfile.h:261
wxString GetHostname()
Definition lockfile.h:256
bool checkUserAndHost()
Definition lockfile.h:288
bool Locked() const
Definition lockfile.h:263
wxString m_username
Definition lockfile.h:281
bool IsLockedByMe()
Definition lockfile.h:236
void UnlockFile()
Unlock and remove the file from the filesystem as long as we still own it.
Definition lockfile.h:163
#define _(s)
static const std::string LockFileExtension
static const std::string LockFilePrefix
#define LCK
Definition lockfile.h:40
Definition of file extensions used in Kicad.