]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/unix/snglinst.cpp
wxMessageBox off the main thread lost result code.
[wxWidgets.git] / src / unix / snglinst.cpp
... / ...
CommitLineData
1///////////////////////////////////////////////////////////////////////////////
2// Name: src/unix/snglinst.cpp
3// Purpose: implements wxSingleInstanceChecker class for Unix using
4// lock files with fcntl(2) or flock(2)
5// Author: Vadim Zeitlin
6// Modified by:
7// Created: 09.06.01
8// Copyright: (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9// Licence: wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24 #pragma hdrstop
25#endif
26
27#if wxUSE_SNGLINST_CHECKER
28
29#ifndef WX_PRECOMP
30 #include "wx/string.h"
31 #include "wx/log.h"
32 #include "wx/intl.h"
33 #include "wx/utils.h" // wxGetHomeDir()
34#endif //WX_PRECOMP
35
36#include "wx/file.h"
37
38#include "wx/snglinst.h"
39
40#include <unistd.h>
41#include <sys/types.h>
42#include <sys/stat.h> // for S_I[RW]USR
43#include <signal.h> // for kill()
44#include <errno.h>
45
46#ifdef HAVE_FCNTL
47 #include <fcntl.h>
48#elif defined(HAVE_FLOCK)
49 #include <sys/file.h>
50#else
51 // normally, wxUSE_SNGLINST_CHECKER must have been reset by configure
52 #error "wxSingleInstanceChecker can't be compiled on this platform"
53#endif // fcntl()/flock()
54
55// ----------------------------------------------------------------------------
56// constants
57// ----------------------------------------------------------------------------
58
59// argument of wxLockFile()
60enum LockOperation
61{
62 LOCK,
63 UNLOCK
64};
65
66// return value of CreateLockFile()
67enum LockResult
68{
69 LOCK_ERROR = -1,
70 LOCK_EXISTS,
71 LOCK_CREATED
72};
73
74// ----------------------------------------------------------------------------
75// private functions: (exclusively) lock/unlock the file
76// ----------------------------------------------------------------------------
77
78#ifdef HAVE_FCNTL
79
80static int wxLockFile(int fd, LockOperation lock)
81{
82 // init the flock parameter struct
83 struct flock fl;
84 fl.l_type = lock == LOCK ? F_WRLCK : F_UNLCK;
85
86 // lock the entire file
87 fl.l_start =
88 fl.l_len =
89 fl.l_whence = 0;
90
91 // is this needed?
92 fl.l_pid = getpid();
93
94 return fcntl(fd, F_SETLK, &fl);
95}
96
97#else // HAVE_FLOCK
98
99static int wxLockFile(int fd, LockOperation lock)
100{
101 return flock(fd, lock == LOCK ? LOCK_EX | LOCK_NB : LOCK_UN);
102}
103
104#endif // fcntl()/flock()
105
106// ----------------------------------------------------------------------------
107// wxSingleInstanceCheckerImpl: the real implementation class
108// ----------------------------------------------------------------------------
109
110class wxSingleInstanceCheckerImpl
111{
112public:
113 wxSingleInstanceCheckerImpl()
114 {
115 m_fdLock = -1;
116 m_pidLocker = 0;
117 }
118
119 bool Create(const wxString& name);
120
121 pid_t GetLockerPID() const { return m_pidLocker; }
122
123 ~wxSingleInstanceCheckerImpl() { Unlock(); }
124
125private:
126 // try to create and lock the file
127 LockResult CreateLockFile();
128
129 // unlock and remove the lock file
130 void Unlock();
131
132 // the descriptor of our lock file, -1 if none
133 int m_fdLock;
134
135 // pid of the process owning the lock file
136 pid_t m_pidLocker;
137
138 // the name of the lock file
139 wxString m_nameLock;
140};
141
142// ============================================================================
143// wxSingleInstanceCheckerImpl implementation
144// ============================================================================
145
146LockResult wxSingleInstanceCheckerImpl::CreateLockFile()
147{
148 // try to open the file
149 m_fdLock = open(m_nameLock.fn_str(),
150 O_WRONLY | O_CREAT | O_EXCL,
151 S_IRUSR | S_IWUSR);
152
153 if ( m_fdLock != -1 )
154 {
155 // try to lock it
156 if ( wxLockFile(m_fdLock, LOCK) == 0 )
157 {
158 // fine, we have the exclusive lock to the file, write our PID
159 // into it
160 m_pidLocker = getpid();
161
162 // use char here, not wxChar!
163 char buf[256]; // enough for any PID size
164 int len = sprintf(buf, "%d", (int)m_pidLocker) + 1;
165
166 if ( write(m_fdLock, buf, len) != len )
167 {
168 wxLogSysError(_("Failed to write to lock file '%s'"),
169 m_nameLock.c_str());
170
171 Unlock();
172
173 return LOCK_ERROR;
174 }
175
176 fsync(m_fdLock);
177
178 // change file's permission so that only this user can access it:
179 if ( chmod(m_nameLock.fn_str(), S_IRUSR | S_IWUSR) != 0 )
180 {
181 wxLogSysError(_("Failed to set permissions on lock file '%s'"),
182 m_nameLock.c_str());
183
184 Unlock();
185
186 return LOCK_ERROR;
187 }
188
189 return LOCK_CREATED;
190 }
191 else // failure: see what exactly happened
192 {
193 close(m_fdLock);
194 m_fdLock = -1;
195
196 if ( errno != EACCES && errno != EAGAIN )
197 {
198 wxLogSysError(_("Failed to lock the lock file '%s'"),
199 m_nameLock.c_str());
200
201 unlink(m_nameLock.fn_str());
202
203 return LOCK_ERROR;
204 }
205 //else: couldn't lock because the lock is held by another process:
206 // this might have happened because of a race condition:
207 // maybe another instance opened and locked the file between
208 // our calls to open() and flock(), so don't give an error
209 }
210 }
211
212 // we didn't create and lock the file
213 return LOCK_EXISTS;
214}
215
216bool wxSingleInstanceCheckerImpl::Create(const wxString& name)
217{
218 m_nameLock = name;
219
220 switch ( CreateLockFile() )
221 {
222 case LOCK_EXISTS:
223 // there is a lock file, check below if it is still valid
224 break;
225
226 case LOCK_CREATED:
227 // nothing more to do
228 return true;
229
230 case LOCK_ERROR:
231 // oops...
232 return false;
233 }
234
235 // Check if the file is owned by current user and has 0600 permissions.
236 // If it doesn't, it's a fake file, possibly meant as a DoS attack, and
237 // so we refuse to touch it:
238 wxStructStat stats;
239 if ( wxStat(name, &stats) != 0 )
240 {
241 wxLogSysError(_("Failed to inspect the lock file '%s'"), name.c_str());
242 return false;
243 }
244 if ( stats.st_uid != getuid() )
245 {
246 wxLogError(_("Lock file '%s' has incorrect owner."), name.c_str());
247 return false;
248 }
249 if ( stats.st_mode != (S_IFREG | S_IRUSR | S_IWUSR) )
250 {
251 wxLogError(_("Lock file '%s' has incorrect permissions."), name.c_str());
252 return false;
253 }
254
255 // try to open the file for reading and get the PID of the process
256 // which has it
257 wxFile file(name, wxFile::read);
258 if ( !file.IsOpened() )
259 {
260 // well, this is really weird - file doesn't exist and we can't
261 // create it
262 //
263 // normally, this just means that we don't have write access to
264 // the directory where we try to create it, so return failure,
265 // even it might also be a rare case of a race condition when
266 // another process managed to open and lock the file and terminate
267 // (erasing it) before we got here, but this should happen so
268 // rarely in practice that we don't care
269 wxLogError(_("Failed to access lock file."));
270
271 return false;
272 }
273
274 char buf[256];
275 ssize_t count = file.Read(buf, WXSIZEOF(buf));
276 if ( count == wxInvalidOffset )
277 {
278 wxLogError(_("Failed to read PID from lock file."));
279 }
280 else
281 {
282 if ( sscanf(buf, "%d", (int *)&m_pidLocker) == 1 )
283 {
284 if ( kill(m_pidLocker, 0) != 0 )
285 {
286 if ( unlink(name.fn_str()) != 0 )
287 {
288 wxLogError(_("Failed to remove stale lock file '%s'."),
289 name.c_str());
290
291 // return true in this case for now...
292 }
293 else
294 {
295 wxLogMessage(_("Deleted stale lock file '%s'."),
296 name.c_str());
297
298 // retry now
299 (void)CreateLockFile();
300 }
301 }
302 //else: the other process is running
303 }
304 else
305 {
306 wxLogWarning(_("Invalid lock file '%s'."), name.c_str());
307 }
308 }
309
310 // return true if we could get the PID of the process owning the lock file
311 // (whether it is still running or not), FALSE otherwise as it is
312 // unexpected
313 return m_pidLocker != 0;
314}
315
316void wxSingleInstanceCheckerImpl::Unlock()
317{
318 if ( m_fdLock != -1 )
319 {
320 if ( unlink(m_nameLock.fn_str()) != 0 )
321 {
322 wxLogSysError(_("Failed to remove lock file '%s'"),
323 m_nameLock.c_str());
324 }
325
326 if ( wxLockFile(m_fdLock, UNLOCK) != 0 )
327 {
328 wxLogSysError(_("Failed to unlock lock file '%s'"),
329 m_nameLock.c_str());
330 }
331
332 if ( close(m_fdLock) != 0 )
333 {
334 wxLogSysError(_("Failed to close lock file '%s'"),
335 m_nameLock.c_str());
336 }
337 }
338
339 m_pidLocker = 0;
340}
341
342// ============================================================================
343// wxSingleInstanceChecker implementation
344// ============================================================================
345
346bool wxSingleInstanceChecker::Create(const wxString& name,
347 const wxString& path)
348{
349 wxASSERT_MSG( !m_impl,
350 wxT("calling wxSingleInstanceChecker::Create() twice?") );
351
352 // must have the file name to create a lock file
353 wxASSERT_MSG( !name.empty(), wxT("lock file name can't be empty") );
354
355 m_impl = new wxSingleInstanceCheckerImpl;
356
357 wxString fullname = path;
358 if ( fullname.empty() )
359 {
360 fullname = wxGetHomeDir();
361 }
362
363 if ( fullname.Last() != wxT('/') )
364 {
365 fullname += wxT('/');
366 }
367
368 fullname << name;
369
370 return m_impl->Create(fullname);
371}
372
373bool wxSingleInstanceChecker::DoIsAnotherRunning() const
374{
375 wxCHECK_MSG( m_impl, false, wxT("must call Create() first") );
376
377 const pid_t lockerPid = m_impl->GetLockerPID();
378
379 if ( !lockerPid )
380 {
381 // we failed to open the lock file, return false as we're definitely
382 // not sure that another our process is running and so it's better not
383 // to prevent this one from starting up
384 return false;
385 }
386
387 // if another instance is running, it must own the lock file - otherwise
388 // we have it and the locker PID is ours one
389 return lockerPid != getpid();
390}
391
392wxSingleInstanceChecker::~wxSingleInstanceChecker()
393{
394 delete m_impl;
395}
396
397#endif // wxUSE_SNGLINST_CHECKER