1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2002
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Conrad Carlen <ccarlen@netscape.com>
24 : * Brendan Eich <brendan@mozilla.org>
25 : * Colin Blake <colin@theblakes.com>
26 : * Javier Pedemonte <pedemont@us.ibm.com>
27 : * Mats Palmgren <mats.palmgren@bredband.net>
28 : *
29 : * Alternatively, the contents of this file may be used under the terms of
30 : * either the GNU General Public License Version 2 or later (the "GPL"), or
31 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 : * in which case the provisions of the GPL or the LGPL are applicable instead
33 : * of those above. If you wish to allow use of your version of this file only
34 : * under the terms of either the GPL or the LGPL, and not to allow others to
35 : * use your version of this file under the terms of the MPL, indicate your
36 : * decision by deleting the provisions above and replace them with the notice
37 : * and other provisions required by the GPL or the LGPL. If you do not delete
38 : * the provisions above, a recipient may use your version of this file under
39 : * the terms of any one of the MPL, the GPL or the LGPL.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 : #include "nsProfileStringTypes.h"
44 : #include "nsProfileLock.h"
45 : #include "nsCOMPtr.h"
46 :
47 : #if defined(XP_MACOSX)
48 : #include <Carbon/Carbon.h>
49 : #include <CoreFoundation/CoreFoundation.h>
50 : #endif
51 :
52 : #ifdef XP_UNIX
53 : #include <unistd.h>
54 : #include <fcntl.h>
55 : #include <errno.h>
56 : #include <signal.h>
57 : #include <stdlib.h>
58 : #include "prnetdb.h"
59 : #include "prsystem.h"
60 : #include "prprf.h"
61 : #include "prenv.h"
62 : #endif
63 :
64 : #ifdef VMS
65 : #include <rmsdef.h>
66 : #endif
67 :
68 : // **********************************************************************
69 : // class nsProfileLock
70 : //
71 : // This code was moved from profile/src/nsProfileAccess.
72 : // **********************************************************************
73 :
74 : #if defined (XP_UNIX)
75 : static bool sDisableSignalHandling = false;
76 : #endif
77 :
78 0 : nsProfileLock::nsProfileLock() :
79 : mHaveLock(false),
80 : mReplacedLockTime(0)
81 : #if defined (XP_WIN)
82 : ,mLockFileHandle(INVALID_HANDLE_VALUE)
83 : #elif defined (XP_OS2)
84 : ,mLockFileHandle(-1)
85 : #elif defined (XP_UNIX)
86 : ,mPidLockFileName(nsnull)
87 0 : ,mLockFileDesc(-1)
88 : #endif
89 : {
90 : #if defined (XP_UNIX)
91 0 : next = prev = this;
92 0 : sDisableSignalHandling = PR_GetEnv("MOZ_DISABLE_SIG_HANDLER") ? true : false;
93 : #endif
94 0 : }
95 :
96 :
97 0 : nsProfileLock::nsProfileLock(nsProfileLock& src)
98 : {
99 0 : *this = src;
100 0 : }
101 :
102 :
103 0 : nsProfileLock& nsProfileLock::operator=(nsProfileLock& rhs)
104 : {
105 0 : Unlock();
106 :
107 0 : mHaveLock = rhs.mHaveLock;
108 0 : rhs.mHaveLock = false;
109 :
110 : #if defined (XP_WIN)
111 : mLockFileHandle = rhs.mLockFileHandle;
112 : rhs.mLockFileHandle = INVALID_HANDLE_VALUE;
113 : #elif defined (XP_OS2)
114 : mLockFileHandle = rhs.mLockFileHandle;
115 : rhs.mLockFileHandle = -1;
116 : #elif defined (XP_UNIX)
117 0 : mLockFileDesc = rhs.mLockFileDesc;
118 0 : rhs.mLockFileDesc = -1;
119 0 : mPidLockFileName = rhs.mPidLockFileName;
120 0 : rhs.mPidLockFileName = nsnull;
121 0 : if (mPidLockFileName)
122 : {
123 : // rhs had a symlink lock, therefore it was on the list.
124 0 : PR_REMOVE_LINK(&rhs);
125 0 : PR_APPEND_LINK(this, &mPidLockList);
126 : }
127 : #endif
128 :
129 0 : return *this;
130 : }
131 :
132 :
133 0 : nsProfileLock::~nsProfileLock()
134 : {
135 0 : Unlock();
136 0 : }
137 :
138 :
139 : #if defined (XP_UNIX)
140 :
141 : static int setupPidLockCleanup;
142 :
143 : PRCList nsProfileLock::mPidLockList =
144 : PR_INIT_STATIC_CLIST(&nsProfileLock::mPidLockList);
145 :
146 0 : void nsProfileLock::RemovePidLockFiles(bool aFatalSignal)
147 : {
148 0 : while (!PR_CLIST_IS_EMPTY(&mPidLockList))
149 : {
150 0 : nsProfileLock *lock = static_cast<nsProfileLock*>(mPidLockList.next);
151 0 : lock->Unlock(aFatalSignal);
152 : }
153 0 : }
154 :
155 : static struct sigaction SIGHUP_oldact;
156 : static struct sigaction SIGINT_oldact;
157 : static struct sigaction SIGQUIT_oldact;
158 : static struct sigaction SIGILL_oldact;
159 : static struct sigaction SIGABRT_oldact;
160 : static struct sigaction SIGSEGV_oldact;
161 : static struct sigaction SIGTERM_oldact;
162 :
163 0 : void nsProfileLock::FatalSignalHandler(int signo
164 : #ifdef SA_SIGINFO
165 : , siginfo_t *info, void *context
166 : #endif
167 : )
168 : {
169 : // Remove any locks still held.
170 0 : RemovePidLockFiles(true);
171 :
172 : // Chain to the old handler, which may exit.
173 0 : struct sigaction *oldact = nsnull;
174 :
175 0 : switch (signo) {
176 : case SIGHUP:
177 0 : oldact = &SIGHUP_oldact;
178 0 : break;
179 : case SIGINT:
180 0 : oldact = &SIGINT_oldact;
181 0 : break;
182 : case SIGQUIT:
183 0 : oldact = &SIGQUIT_oldact;
184 0 : break;
185 : case SIGILL:
186 0 : oldact = &SIGILL_oldact;
187 0 : break;
188 : case SIGABRT:
189 0 : oldact = &SIGABRT_oldact;
190 0 : break;
191 : case SIGSEGV:
192 0 : oldact = &SIGSEGV_oldact;
193 0 : break;
194 : case SIGTERM:
195 0 : oldact = &SIGTERM_oldact;
196 0 : break;
197 : default:
198 0 : NS_NOTREACHED("bad signo");
199 0 : break;
200 : }
201 :
202 0 : if (oldact) {
203 0 : if (oldact->sa_handler == SIG_DFL) {
204 : // Make sure the default sig handler is executed
205 : // We need it to get Mozilla to dump core.
206 0 : sigaction(signo,oldact,NULL);
207 :
208 : // Now that we've restored the default handler, unmask the
209 : // signal and invoke it.
210 :
211 : sigset_t unblock_sigs;
212 0 : sigemptyset(&unblock_sigs);
213 0 : sigaddset(&unblock_sigs, signo);
214 :
215 0 : sigprocmask(SIG_UNBLOCK, &unblock_sigs, NULL);
216 :
217 0 : raise(signo);
218 : }
219 : #ifdef SA_SIGINFO
220 0 : else if (oldact->sa_sigaction &&
221 : (oldact->sa_flags & SA_SIGINFO) == SA_SIGINFO) {
222 0 : oldact->sa_sigaction(signo, info, context);
223 : }
224 : #endif
225 0 : else if (oldact->sa_handler && oldact->sa_handler != SIG_IGN)
226 : {
227 0 : oldact->sa_handler(signo);
228 : }
229 : }
230 :
231 : // Backstop exit call, just in case.
232 0 : _exit(signo);
233 : }
234 :
235 0 : nsresult nsProfileLock::LockWithFcntl(nsILocalFile *aLockFile)
236 : {
237 0 : nsresult rv = NS_OK;
238 :
239 0 : nsCAutoString lockFilePath;
240 0 : rv = aLockFile->GetNativePath(lockFilePath);
241 0 : if (NS_FAILED(rv)) {
242 0 : NS_ERROR("Could not get native path");
243 0 : return rv;
244 : }
245 :
246 0 : aLockFile->GetLastModifiedTime(&mReplacedLockTime);
247 :
248 0 : mLockFileDesc = open(PromiseFlatCString(lockFilePath).get(),
249 0 : O_WRONLY | O_CREAT | O_TRUNC, 0666);
250 0 : if (mLockFileDesc != -1)
251 : {
252 : struct flock lock;
253 0 : lock.l_start = 0;
254 0 : lock.l_len = 0; // len = 0 means entire file
255 0 : lock.l_type = F_WRLCK;
256 0 : lock.l_whence = SEEK_SET;
257 :
258 : // If fcntl(F_GETLK) fails then the server does not support/allow fcntl(),
259 : // return failure rather than access denied in this case so we fallback
260 : // to using a symlink lock, bug 303633.
261 0 : struct flock testlock = lock;
262 0 : if (fcntl(mLockFileDesc, F_GETLK, &testlock) == -1)
263 : {
264 0 : close(mLockFileDesc);
265 0 : mLockFileDesc = -1;
266 0 : rv = NS_ERROR_FAILURE;
267 : }
268 0 : else if (fcntl(mLockFileDesc, F_SETLK, &lock) == -1)
269 : {
270 0 : close(mLockFileDesc);
271 0 : mLockFileDesc = -1;
272 :
273 : // With OS X, on NFS, errno == ENOTSUP
274 : // XXX Check for that and return specific rv for it?
275 : #ifdef DEBUG
276 0 : printf("fcntl(F_SETLK) failed. errno = %d\n", errno);
277 : #endif
278 0 : if (errno == EAGAIN || errno == EACCES)
279 0 : rv = NS_ERROR_FILE_ACCESS_DENIED;
280 : else
281 0 : rv = NS_ERROR_FAILURE;
282 : }
283 : else
284 0 : mHaveLock = true;
285 : }
286 : else
287 : {
288 0 : NS_ERROR("Failed to open lock file.");
289 0 : rv = NS_ERROR_FAILURE;
290 : }
291 0 : return rv;
292 : }
293 :
294 0 : static bool IsSymlinkStaleLock(struct in_addr* aAddr, const char* aFileName,
295 : bool aHaveFcntlLock)
296 : {
297 : // the link exists; see if it's from this machine, and if
298 : // so if the process is still active
299 : char buf[1024];
300 0 : int len = readlink(aFileName, buf, sizeof buf - 1);
301 0 : if (len > 0)
302 : {
303 0 : buf[len] = '\0';
304 0 : char *colon = strchr(buf, ':');
305 0 : if (colon)
306 : {
307 0 : *colon++ = '\0';
308 0 : unsigned long addr = inet_addr(buf);
309 0 : if (addr != (unsigned long) -1)
310 : {
311 0 : if (colon[0] == '+' && aHaveFcntlLock) {
312 : // This lock was placed by a Firefox build which would have
313 : // taken the fnctl lock, and we've already taken the fcntl lock,
314 : // so the process that created this obsolete lock must be gone
315 0 : return true;
316 : }
317 :
318 0 : char *after = nsnull;
319 0 : pid_t pid = strtol(colon, &after, 0);
320 0 : if (pid != 0 && *after == '\0')
321 : {
322 0 : if (addr != aAddr->s_addr)
323 : {
324 : // Remote lock: give up even if stuck.
325 0 : return false;
326 : }
327 :
328 : // kill(pid,0) is a neat trick to check if a
329 : // process exists
330 0 : if (kill(pid, 0) == 0 || errno != ESRCH)
331 : {
332 : // Local process appears to be alive, ass-u-me it
333 : // is another Mozilla instance, or a compatible
334 : // derivative, that's currently using the profile.
335 : // XXX need an "are you Mozilla?" protocol
336 0 : return false;
337 : }
338 : }
339 : }
340 : }
341 : }
342 0 : return true;
343 : }
344 :
345 0 : nsresult nsProfileLock::LockWithSymlink(nsILocalFile *aLockFile, bool aHaveFcntlLock)
346 : {
347 : nsresult rv;
348 0 : nsCAutoString lockFilePath;
349 0 : rv = aLockFile->GetNativePath(lockFilePath);
350 0 : if (NS_FAILED(rv)) {
351 0 : NS_ERROR("Could not get native path");
352 0 : return rv;
353 : }
354 :
355 : // don't replace an existing lock time if fcntl already got one
356 0 : if (!mReplacedLockTime)
357 0 : aLockFile->GetLastModifiedTimeOfLink(&mReplacedLockTime);
358 :
359 : struct in_addr inaddr;
360 0 : inaddr.s_addr = htonl(INADDR_LOOPBACK);
361 :
362 : char hostname[256];
363 0 : PRStatus status = PR_GetSystemInfo(PR_SI_HOSTNAME, hostname, sizeof hostname);
364 0 : if (status == PR_SUCCESS)
365 : {
366 : char netdbbuf[PR_NETDB_BUF_SIZE];
367 : PRHostEnt hostent;
368 0 : status = PR_GetHostByName(hostname, netdbbuf, sizeof netdbbuf, &hostent);
369 0 : if (status == PR_SUCCESS)
370 0 : memcpy(&inaddr, hostent.h_addr, sizeof inaddr);
371 : }
372 :
373 : char *signature =
374 : PR_smprintf("%s:%s%lu", inet_ntoa(inaddr), aHaveFcntlLock ? "+" : "",
375 0 : (unsigned long)getpid());
376 0 : const nsPromiseFlatCString& flat = PromiseFlatCString(lockFilePath);
377 0 : const char *fileName = flat.get();
378 0 : int symlink_rv, symlink_errno = 0, tries = 0;
379 :
380 : // use ns4.x-compatible symlinks if the FS supports them
381 0 : while ((symlink_rv = symlink(signature, fileName)) < 0)
382 : {
383 0 : symlink_errno = errno;
384 0 : if (symlink_errno != EEXIST)
385 0 : break;
386 :
387 0 : if (!IsSymlinkStaleLock(&inaddr, fileName, aHaveFcntlLock))
388 0 : break;
389 :
390 : // Lock seems to be bogus: try to claim it. Give up after a large
391 : // number of attempts (100 comes from the 4.x codebase).
392 0 : (void) unlink(fileName);
393 0 : if (++tries > 100)
394 0 : break;
395 : }
396 :
397 0 : PR_smprintf_free(signature);
398 0 : signature = nsnull;
399 :
400 0 : if (symlink_rv == 0)
401 : {
402 : // We exclusively created the symlink: record its name for eventual
403 : // unlock-via-unlink.
404 0 : rv = NS_OK;
405 0 : mHaveLock = true;
406 0 : mPidLockFileName = strdup(fileName);
407 0 : if (mPidLockFileName)
408 : {
409 0 : PR_APPEND_LINK(this, &mPidLockList);
410 0 : if (!setupPidLockCleanup++)
411 : {
412 : // Clean up on normal termination.
413 : // This instanciates a dummy class, and will trigger the class
414 : // destructor when libxul is unloaded. This is equivalent to atexit(),
415 : // but gracefully handles dlclose().
416 0 : static RemovePidLockFilesExiting r;
417 :
418 : // Clean up on abnormal termination, using POSIX sigaction.
419 : // Don't arm a handler if the signal is being ignored, e.g.,
420 : // because mozilla is run via nohup.
421 0 : if (!sDisableSignalHandling) {
422 : struct sigaction act, oldact;
423 : #ifdef SA_SIGINFO
424 0 : act.sa_sigaction = FatalSignalHandler;
425 0 : act.sa_flags = SA_SIGINFO;
426 : #else
427 : act.sa_handler = FatalSignalHandler;
428 : #endif
429 0 : sigfillset(&act.sa_mask);
430 :
431 : #define CATCH_SIGNAL(signame) \
432 : PR_BEGIN_MACRO \
433 : if (sigaction(signame, NULL, &oldact) == 0 && \
434 : oldact.sa_handler != SIG_IGN) \
435 : { \
436 : sigaction(signame, &act, &signame##_oldact); \
437 : } \
438 : PR_END_MACRO
439 :
440 0 : CATCH_SIGNAL(SIGHUP);
441 0 : CATCH_SIGNAL(SIGINT);
442 0 : CATCH_SIGNAL(SIGQUIT);
443 0 : CATCH_SIGNAL(SIGILL);
444 0 : CATCH_SIGNAL(SIGABRT);
445 0 : CATCH_SIGNAL(SIGSEGV);
446 0 : CATCH_SIGNAL(SIGTERM);
447 :
448 : #undef CATCH_SIGNAL
449 : }
450 : }
451 : }
452 : }
453 0 : else if (symlink_errno == EEXIST)
454 0 : rv = NS_ERROR_FILE_ACCESS_DENIED;
455 : else
456 : {
457 : #ifdef DEBUG
458 0 : printf("symlink() failed. errno = %d\n", errno);
459 : #endif
460 0 : rv = NS_ERROR_FAILURE;
461 : }
462 0 : return rv;
463 : }
464 : #endif /* XP_UNIX */
465 :
466 0 : nsresult nsProfileLock::GetReplacedLockTime(PRInt64 *aResult) {
467 0 : *aResult = mReplacedLockTime;
468 0 : return NS_OK;
469 : }
470 :
471 0 : nsresult nsProfileLock::Lock(nsILocalFile* aProfileDir,
472 : nsIProfileUnlocker* *aUnlocker)
473 : {
474 : #if defined (XP_MACOSX)
475 : NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, ".parentlock");
476 : NS_NAMED_LITERAL_STRING(OLD_LOCKFILE_NAME, "parent.lock");
477 : #elif defined (XP_UNIX)
478 0 : NS_NAMED_LITERAL_STRING(OLD_LOCKFILE_NAME, "lock");
479 0 : NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, ".parentlock");
480 : #else
481 : NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, "parent.lock");
482 : #endif
483 :
484 : nsresult rv;
485 0 : if (aUnlocker)
486 0 : *aUnlocker = nsnull;
487 :
488 0 : NS_ENSURE_STATE(!mHaveLock);
489 :
490 : bool isDir;
491 0 : rv = aProfileDir->IsDirectory(&isDir);
492 0 : if (NS_FAILED(rv))
493 0 : return rv;
494 0 : if (!isDir)
495 0 : return NS_ERROR_FILE_NOT_DIRECTORY;
496 :
497 0 : nsCOMPtr<nsILocalFile> lockFile;
498 0 : rv = aProfileDir->Clone((nsIFile **)((void **)getter_AddRefs(lockFile)));
499 0 : if (NS_FAILED(rv))
500 0 : return rv;
501 :
502 0 : rv = lockFile->Append(LOCKFILE_NAME);
503 0 : if (NS_FAILED(rv))
504 0 : return rv;
505 :
506 : #if defined(XP_MACOSX)
507 : // First, try locking using fcntl. It is more reliable on
508 : // a local machine, but may not be supported by an NFS server.
509 :
510 : rv = LockWithFcntl(lockFile);
511 : if (NS_FAILED(rv) && (rv != NS_ERROR_FILE_ACCESS_DENIED))
512 : {
513 : // If that failed for any reason other than NS_ERROR_FILE_ACCESS_DENIED,
514 : // assume we tried an NFS that does not support it. Now, try with symlink.
515 : rv = LockWithSymlink(lockFile, false);
516 : }
517 :
518 : if (NS_SUCCEEDED(rv))
519 : {
520 : // Check for the old-style lock used by pre-mozilla 1.3 builds.
521 : // Those builds used an earlier check to prevent the application
522 : // from launching if another instance was already running. Because
523 : // of that, we don't need to create an old-style lock as well.
524 : struct LockProcessInfo
525 : {
526 : ProcessSerialNumber psn;
527 : unsigned long launchDate;
528 : };
529 :
530 : PRFileDesc *fd = nsnull;
531 : PRInt32 ioBytes;
532 : ProcessInfoRec processInfo;
533 : LockProcessInfo lockProcessInfo;
534 :
535 : rv = lockFile->SetLeafName(OLD_LOCKFILE_NAME);
536 : if (NS_FAILED(rv))
537 : return rv;
538 : rv = lockFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
539 : if (NS_SUCCEEDED(rv))
540 : {
541 : ioBytes = PR_Read(fd, &lockProcessInfo, sizeof(LockProcessInfo));
542 : PR_Close(fd);
543 :
544 : if (ioBytes == sizeof(LockProcessInfo))
545 : {
546 : #ifdef __LP64__
547 : processInfo.processAppRef = NULL;
548 : #else
549 : processInfo.processAppSpec = NULL;
550 : #endif
551 : processInfo.processName = NULL;
552 : processInfo.processInfoLength = sizeof(ProcessInfoRec);
553 : if (::GetProcessInformation(&lockProcessInfo.psn, &processInfo) == noErr &&
554 : processInfo.processLaunchDate == lockProcessInfo.launchDate)
555 : {
556 : return NS_ERROR_FILE_ACCESS_DENIED;
557 : }
558 : }
559 : else
560 : {
561 : NS_WARNING("Could not read lock file - ignoring lock");
562 : }
563 : }
564 : rv = NS_OK; // Don't propagate error from OpenNSPRFileDesc.
565 : }
566 : #elif defined(XP_UNIX)
567 : // Get the old lockfile name
568 0 : nsCOMPtr<nsILocalFile> oldLockFile;
569 0 : rv = aProfileDir->Clone((nsIFile **)((void **)getter_AddRefs(oldLockFile)));
570 0 : if (NS_FAILED(rv))
571 0 : return rv;
572 0 : rv = oldLockFile->Append(OLD_LOCKFILE_NAME);
573 0 : if (NS_FAILED(rv))
574 0 : return rv;
575 :
576 : // First, try locking using fcntl. It is more reliable on
577 : // a local machine, but may not be supported by an NFS server.
578 0 : rv = LockWithFcntl(lockFile);
579 0 : if (NS_SUCCEEDED(rv)) {
580 : // Check to see whether there is a symlink lock held by an older
581 : // Firefox build, and also place our own symlink lock --- but
582 : // mark it "obsolete" so that other newer builds can break the lock
583 : // if they obtain the fcntl lock
584 0 : rv = LockWithSymlink(oldLockFile, true);
585 :
586 : // If the symlink failed for some reason other than it already
587 : // exists, then something went wrong e.g. the file system
588 : // doesn't support symlinks, or we don't have permission to
589 : // create a symlink there. In such cases we should just
590 : // continue because it's unlikely there is an old build
591 : // running with a symlink there and we've already successfully
592 : // placed a fcntl lock.
593 0 : if (rv != NS_ERROR_FILE_ACCESS_DENIED)
594 0 : rv = NS_OK;
595 : }
596 0 : else if (rv != NS_ERROR_FILE_ACCESS_DENIED)
597 : {
598 : // If that failed for any reason other than NS_ERROR_FILE_ACCESS_DENIED,
599 : // assume we tried an NFS that does not support it. Now, try with symlink
600 : // using the old symlink path
601 0 : rv = LockWithSymlink(oldLockFile, false);
602 : }
603 :
604 : #elif defined(XP_WIN)
605 : nsAutoString filePath;
606 : rv = lockFile->GetPath(filePath);
607 : if (NS_FAILED(rv))
608 : return rv;
609 :
610 : lockFile->GetLastModifiedTime(&mReplacedLockTime);
611 :
612 : // always create the profile lock and never delete it so we can use its
613 : // modification timestamp to detect startup crashes
614 : mLockFileHandle = CreateFileW(filePath.get(),
615 : GENERIC_READ | GENERIC_WRITE,
616 : 0, // no sharing - of course
617 : nsnull,
618 : CREATE_ALWAYS,
619 : nsnull,
620 : nsnull);
621 : if (mLockFileHandle == INVALID_HANDLE_VALUE) {
622 : // XXXbsmedberg: provide a profile-unlocker here!
623 : return NS_ERROR_FILE_ACCESS_DENIED;
624 : }
625 : #elif defined(XP_OS2)
626 : nsCAutoString filePath;
627 : rv = lockFile->GetNativePath(filePath);
628 : if (NS_FAILED(rv))
629 : return rv;
630 :
631 : lockFile->GetLastModifiedTime(&mReplacedLockTime);
632 :
633 : ULONG ulAction = 0;
634 : APIRET rc;
635 : rc = DosOpen(filePath.get(),
636 : &mLockFileHandle,
637 : &ulAction,
638 : 0,
639 : FILE_NORMAL,
640 : OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
641 : OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
642 : 0 );
643 : if (rc != NO_ERROR)
644 : {
645 : mLockFileHandle = -1;
646 : return NS_ERROR_FILE_ACCESS_DENIED;
647 : }
648 : #elif defined(VMS)
649 : nsCAutoString filePath;
650 : rv = lockFile->GetNativePath(filePath);
651 : if (NS_FAILED(rv))
652 : return rv;
653 :
654 : lockFile->GetLastModifiedTime(&mReplacedLockTime);
655 :
656 : mLockFileDesc = open_noshr(filePath.get(), O_CREAT, 0666);
657 : if (mLockFileDesc == -1)
658 : {
659 : if ((errno == EVMSERR) && (vaxc$errno == RMS$_FLK))
660 : {
661 : return NS_ERROR_FILE_ACCESS_DENIED;
662 : }
663 : else
664 : {
665 : NS_ERROR("Failed to open lock file.");
666 : return NS_ERROR_FAILURE;
667 : }
668 : }
669 : #endif
670 :
671 0 : mHaveLock = true;
672 :
673 0 : return rv;
674 : }
675 :
676 :
677 0 : nsresult nsProfileLock::Unlock(bool aFatalSignal)
678 : {
679 0 : nsresult rv = NS_OK;
680 :
681 0 : if (mHaveLock)
682 : {
683 : #if defined (XP_WIN)
684 : if (mLockFileHandle != INVALID_HANDLE_VALUE)
685 : {
686 : CloseHandle(mLockFileHandle);
687 : mLockFileHandle = INVALID_HANDLE_VALUE;
688 : }
689 : #elif defined (XP_OS2)
690 : if (mLockFileHandle != -1)
691 : {
692 : DosClose(mLockFileHandle);
693 : mLockFileHandle = -1;
694 : }
695 : #elif defined (XP_UNIX)
696 0 : if (mPidLockFileName)
697 : {
698 0 : PR_REMOVE_LINK(this);
699 0 : (void) unlink(mPidLockFileName);
700 :
701 : // Only free mPidLockFileName if we're not in the fatal signal
702 : // handler. The problem is that a call to free() might be the
703 : // cause of this fatal signal. If so, calling free() might cause
704 : // us to wait on the malloc implementation's lock. We're already
705 : // holding this lock, so we'll deadlock. See bug 522332.
706 0 : if (!aFatalSignal)
707 0 : free(mPidLockFileName);
708 0 : mPidLockFileName = nsnull;
709 : }
710 0 : else if (mLockFileDesc != -1)
711 : {
712 0 : close(mLockFileDesc);
713 0 : mLockFileDesc = -1;
714 : // Don't remove it
715 : }
716 : #endif
717 :
718 0 : mHaveLock = false;
719 : }
720 :
721 0 : return rv;
722 : }
|