2003-01-09 13:36:05 +01:00
|
|
|
|
/* dotlock.c - dotfile locking
|
2011-02-04 12:57:53 +01:00
|
|
|
|
* Copyright (C) 1998, 2000, 2001, 2003, 2004,
|
2011-09-23 14:43:58 +02:00
|
|
|
|
* 2005, 2006, 2008, 2010, 2011 Free Software Foundation, Inc.
|
2003-01-09 13:36:05 +01:00
|
|
|
|
*
|
2015-04-24 16:42:28 +02:00
|
|
|
|
* This file is part of GnuPG.
|
2003-01-09 13:36:05 +01:00
|
|
|
|
*
|
2017-02-24 13:48:28 +01:00
|
|
|
|
* GnuPG is free software; you can redistribute and/or modify this
|
|
|
|
|
* part of GnuPG under the terms of either
|
2011-09-30 12:52:11 +02:00
|
|
|
|
*
|
|
|
|
|
* - the GNU Lesser General Public License as published by the Free
|
|
|
|
|
* Software Foundation; either version 3 of the License, or (at
|
|
|
|
|
* your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* or
|
|
|
|
|
*
|
|
|
|
|
* - 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.
|
|
|
|
|
*
|
|
|
|
|
* or both in parallel, as here.
|
2003-01-09 13:36:05 +01:00
|
|
|
|
*
|
2015-04-24 16:42:28 +02:00
|
|
|
|
* GnuPG is distributed in the hope that it will be useful, but
|
2006-10-02 13:54:35 +02:00
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
2011-09-30 12:52:11 +02:00
|
|
|
|
* General Public License for more details.
|
2003-01-09 13:36:05 +01:00
|
|
|
|
*
|
2011-09-30 12:52:11 +02:00
|
|
|
|
* You should have received a copies of the GNU General Public License
|
|
|
|
|
* and the GNU Lesser General Public License along with this program;
|
2016-11-05 12:02:19 +01:00
|
|
|
|
* if not, see <https://www.gnu.org/licenses/>.
|
2011-10-24 11:38:17 +02:00
|
|
|
|
*
|
|
|
|
|
* ALTERNATIVELY, this file may be distributed under the terms of the
|
|
|
|
|
* following license, in which case the provisions of this license are
|
|
|
|
|
* required INSTEAD OF the GNU Lesser General License or the GNU
|
|
|
|
|
* General Public License. If you wish to allow use of your version of
|
|
|
|
|
* this file only under the terms of the GNU Lesser General License or
|
|
|
|
|
* the GNU General Public License, and not to allow others to use your
|
|
|
|
|
* version of this file under the terms of the following license,
|
|
|
|
|
* indicate your decision by deleting this paragraph and the license
|
|
|
|
|
* below.
|
|
|
|
|
*
|
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
|
* are met:
|
|
|
|
|
*
|
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
|
* notice, and the entire permission notice in its entirety,
|
|
|
|
|
* including the disclaimer of warranties.
|
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
|
* 3. The name of the author may not be used to endorse or promote
|
|
|
|
|
* products derived from this software without specific prior
|
|
|
|
|
* written permission.
|
|
|
|
|
*
|
2012-06-05 19:29:22 +02:00
|
|
|
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
2011-10-24 11:38:17 +02:00
|
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
|
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
|
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
|
|
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
2003-01-09 13:36:05 +01:00
|
|
|
|
*/
|
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
/*
|
|
|
|
|
Overview:
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This module implements advisory file locking in a portable way.
|
|
|
|
|
Due to the problems with POSIX fcntl locking a separate lock file
|
|
|
|
|
is used. It would be possible to use fcntl locking on this lock
|
|
|
|
|
file and thus avoid the weird auto unlock bug of POSIX while still
|
|
|
|
|
having an unproved better performance of fcntl locking. However
|
|
|
|
|
there are still problems left, thus we resort to use a hardlink
|
|
|
|
|
which has the well defined property that a link call will fail if
|
|
|
|
|
the target file already exists.
|
|
|
|
|
|
|
|
|
|
Given that hardlinks are also available on NTFS file systems since
|
|
|
|
|
Windows XP; it will be possible to enhance this module to use
|
|
|
|
|
hardlinks even on Windows and thus allow Windows and Posix clients
|
|
|
|
|
to use locking on the same directory. This is not yet implemented;
|
|
|
|
|
instead we use a lockfile on Windows along with W32 style file
|
|
|
|
|
locking.
|
|
|
|
|
|
|
|
|
|
On FAT file systems hardlinks are not supported. Thus this method
|
|
|
|
|
does not work. Our solution is to use a O_EXCL locking instead.
|
|
|
|
|
Querying the type of the file system is not easy to do in a
|
|
|
|
|
portable way (e.g. Linux has a statfs, BSDs have a the same call
|
|
|
|
|
but using different structures and constants). What we do instead
|
|
|
|
|
is to check at runtime whether link(2) works for a specific lock
|
|
|
|
|
file.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
How to use:
|
|
|
|
|
===========
|
|
|
|
|
|
|
|
|
|
At program initialization time, the module should be explicitly
|
|
|
|
|
initialized:
|
|
|
|
|
|
2011-09-28 15:41:58 +02:00
|
|
|
|
dotlock_create (NULL, 0);
|
2011-09-27 17:18:56 +02:00
|
|
|
|
|
|
|
|
|
This installs an atexit handler and may also initialize mutex etc.
|
|
|
|
|
It is optional for non-threaded applications. Only the first call
|
2011-09-29 15:27:01 +02:00
|
|
|
|
has an effect. This needs to be done before any extra threads are
|
|
|
|
|
started.
|
2011-09-27 17:18:56 +02:00
|
|
|
|
|
|
|
|
|
To create a lock file (which prepares it but does not take the
|
|
|
|
|
lock) you do:
|
|
|
|
|
|
|
|
|
|
dotlock_t h
|
|
|
|
|
|
2011-09-28 15:41:58 +02:00
|
|
|
|
h = dotlock_create (fname, 0);
|
2011-09-27 17:18:56 +02:00
|
|
|
|
if (!h)
|
|
|
|
|
error ("error creating lock file: %s\n", strerror (errno));
|
|
|
|
|
|
|
|
|
|
It is important to handle the error. For example on a read-only
|
|
|
|
|
file system a lock can't be created (but is usually not needed).
|
|
|
|
|
FNAME is the file you want to lock; the actual lockfile is that
|
2011-09-29 15:27:01 +02:00
|
|
|
|
name with the suffix ".lock" appended. On success a handle to be
|
|
|
|
|
used with the other functions is returned or NULL on error. Note
|
|
|
|
|
that the handle shall only be used by one thread at a time. This
|
|
|
|
|
function creates a unique file temporary file (".#lk*") in the same
|
|
|
|
|
directory as FNAME and returns a handle for further operations.
|
Spelling cleanup.
No functional changes, just fixing minor spelling issues.
---
Most of these were identified from the command line by running:
codespell \
--ignore-words-list fpr,stati,keyserver,keyservers,asign,cas,iff,ifset \
--skip '*.po,ChangeLog*,help.*.txt,*.jpg,*.eps,*.pdf,*.png,*.gpg,*.asc' \
doc g13 g10 kbx agent artwork scd tests tools am common dirmngr sm \
NEWS README README.maint TODO
Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2020-02-18 15:34:42 +01:00
|
|
|
|
The module keeps track of these unique files so that they will be
|
2011-09-29 15:27:01 +02:00
|
|
|
|
unlinked using the atexit handler. If you don't need the lock file
|
|
|
|
|
anymore, you may also explicitly remove it with a call to:
|
2011-09-27 17:18:56 +02:00
|
|
|
|
|
|
|
|
|
dotlock_destroy (h);
|
|
|
|
|
|
|
|
|
|
To actually lock the file, you use:
|
|
|
|
|
|
|
|
|
|
if (dotlock_take (h, -1))
|
|
|
|
|
error ("error taking lock: %s\n", strerror (errno));
|
|
|
|
|
|
|
|
|
|
This function will wait until the lock is acquired. If an
|
|
|
|
|
unexpected error occurs if will return non-zero and set ERRNO. If
|
2011-09-28 11:47:40 +02:00
|
|
|
|
you pass (0) instead of (-1) the function does not wait in case the
|
|
|
|
|
file is already locked but returns -1 and sets ERRNO to EACCES.
|
|
|
|
|
Any other positive value for the second parameter is considered a
|
2018-10-24 21:56:18 +02:00
|
|
|
|
timeout value in milliseconds.
|
2011-09-27 17:18:56 +02:00
|
|
|
|
|
|
|
|
|
To release the lock you call:
|
|
|
|
|
|
|
|
|
|
if (dotlock_release (h))
|
|
|
|
|
error ("error releasing lock: %s\n", strerror (errno));
|
|
|
|
|
|
2011-09-29 16:51:48 +02:00
|
|
|
|
or, if the lock file is not anymore needed, you may just call
|
|
|
|
|
dotlock_destroy. However dotlock_release does some extra checks
|
|
|
|
|
before releasing the lock and prints diagnostics to help detecting
|
|
|
|
|
bugs.
|
2011-09-27 17:18:56 +02:00
|
|
|
|
|
|
|
|
|
If you want to explicitly destroy all lock files you may call
|
|
|
|
|
|
|
|
|
|
dotlock_remove_lockfiles ();
|
|
|
|
|
|
|
|
|
|
which is the core of the installed atexit handler. In case your
|
|
|
|
|
application wants to disable locking completely it may call
|
|
|
|
|
|
|
|
|
|
disable_locking ()
|
|
|
|
|
|
|
|
|
|
before any locks are created.
|
|
|
|
|
|
2011-09-29 16:51:48 +02:00
|
|
|
|
There are two convenience functions to store an integer (e.g. a
|
|
|
|
|
file descriptor) value with the handle:
|
|
|
|
|
|
|
|
|
|
void dotlock_set_fd (dotlock_t h, int fd);
|
|
|
|
|
int dotlock_get_fd (dotlock_t h);
|
|
|
|
|
|
|
|
|
|
If nothing has been stored dotlock_get_fd returns -1.
|
|
|
|
|
|
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
|
|
|
|
|
How to build:
|
|
|
|
|
=============
|
|
|
|
|
|
|
|
|
|
This module was originally developed for GnuPG but later changed to
|
|
|
|
|
allow its use without any GnuPG dependency. If you want to use it
|
|
|
|
|
with you application you may simply use it and it should figure out
|
|
|
|
|
most things automagically.
|
|
|
|
|
|
|
|
|
|
You may use the common config.h file to pass macros, but take care
|
|
|
|
|
to pass -DHAVE_CONFIG_H to the compiler. Macros used by this
|
|
|
|
|
module are:
|
|
|
|
|
|
2011-09-29 15:27:01 +02:00
|
|
|
|
DOTLOCK_USE_PTHREAD - Define if POSIX threads are in use.
|
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
DOTLOCK_GLIB_LOGGING - Define this to use Glib logging functions.
|
|
|
|
|
|
2011-09-30 09:45:21 +02:00
|
|
|
|
DOTLOCK_EXT_SYM_PREFIX - Prefix all external symbols with the
|
|
|
|
|
string to which this macro evaluates.
|
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
GNUPG_MAJOR_VERSION - Defined when used by GnuPG.
|
|
|
|
|
|
2011-09-30 09:45:21 +02:00
|
|
|
|
HAVE_DOSISH_SYSTEM - Defined for Windows etc. Will be
|
|
|
|
|
automatically defined if a the target is
|
|
|
|
|
Windows.
|
|
|
|
|
|
|
|
|
|
HAVE_POSIX_SYSTEM - Internally defined to !HAVE_DOSISH_SYSTEM.
|
2011-09-27 17:18:56 +02:00
|
|
|
|
|
2011-09-30 09:45:21 +02:00
|
|
|
|
HAVE_SIGNAL_H - Should be defined on Posix systems. If config.h
|
|
|
|
|
is not used defaults to defined.
|
2011-09-27 17:18:56 +02:00
|
|
|
|
|
|
|
|
|
DIRSEP_C - Separation character for file name parts.
|
|
|
|
|
Usually not redefined.
|
2011-09-30 09:45:21 +02:00
|
|
|
|
|
|
|
|
|
EXTSEP_S - Separation string for file name suffixes.
|
2011-09-27 17:18:56 +02:00
|
|
|
|
Usually not redefined.
|
|
|
|
|
|
|
|
|
|
Note that there is a test program t-dotlock which has compile
|
|
|
|
|
instructions at its end. At least for SMBFS and CIFS it is
|
|
|
|
|
important that 64 bit versions of stat are used; most programming
|
|
|
|
|
environments do this these days, just in case you want to compile
|
|
|
|
|
it on the command line, remember to pass -D_FILE_OFFSET_BITS=64
|
|
|
|
|
|
|
|
|
|
|
2011-09-29 15:27:01 +02:00
|
|
|
|
Bugs:
|
|
|
|
|
=====
|
|
|
|
|
|
|
|
|
|
On Windows this module is not yet thread-safe.
|
|
|
|
|
|
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
Miscellaneous notes:
|
|
|
|
|
====================
|
|
|
|
|
|
|
|
|
|
On hardlinks:
|
|
|
|
|
- Hardlinks are supported under Windows with NTFS since XP/Server2003.
|
|
|
|
|
- In Linux 2.6.33 both SMBFS and CIFS seem to support hardlinks.
|
|
|
|
|
- NFS supports hard links. But there are solvable problems.
|
|
|
|
|
- FAT does not support links
|
|
|
|
|
|
|
|
|
|
On the file locking API:
|
|
|
|
|
- CIFS on Linux 2.6.33 supports several locking methods.
|
|
|
|
|
SMBFS seems not to support locking. No closer checks done.
|
|
|
|
|
- NFS supports Posix locks. flock is emulated in the server.
|
|
|
|
|
However there are a couple of problems; see below.
|
|
|
|
|
- FAT does not support locks.
|
|
|
|
|
- An advantage of fcntl locking is that R/W locks can be
|
|
|
|
|
implemented which is not easy with a straight lock file.
|
|
|
|
|
|
|
|
|
|
On O_EXCL:
|
|
|
|
|
- Does not work reliable on NFS
|
|
|
|
|
- Should work on CIFS and SMBFS but how can we delete lockfiles?
|
|
|
|
|
|
|
|
|
|
On NFS problems:
|
|
|
|
|
- Locks vanish if the server crashes and reboots.
|
|
|
|
|
- Client crashes keep the lock in the server until the client
|
|
|
|
|
re-connects.
|
|
|
|
|
- Communication problems may return unreliable error codes. The
|
|
|
|
|
MUA Postfix's workaround is to compare the link count after
|
|
|
|
|
seeing an error for link. However that gives a race. If using a
|
|
|
|
|
unique file to link to a lockfile and using stat to check the
|
|
|
|
|
link count instead of looking at the error return of link(2) is
|
|
|
|
|
the best solution.
|
|
|
|
|
- O_EXCL seems to have a race and may re-create a file anyway.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
# include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Some quick replacements for stuff we usually expect to be defined
|
|
|
|
|
in config.h. Define HAVE_POSIX_SYSTEM for better readability. */
|
|
|
|
|
#if !defined (HAVE_DOSISH_SYSTEM) && defined(_WIN32)
|
|
|
|
|
# define HAVE_DOSISH_SYSTEM 1
|
|
|
|
|
#endif
|
|
|
|
|
#if !defined (HAVE_DOSISH_SYSTEM) && !defined (HAVE_POSIX_SYSTEM)
|
|
|
|
|
# define HAVE_POSIX_SYSTEM 1
|
|
|
|
|
#endif
|
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
/* With no config.h assume that we have sitgnal.h. */
|
|
|
|
|
#if !defined (HAVE_CONFIG_H) && defined (HAVE_POSIX_SYSTEM)
|
|
|
|
|
# define HAVE_SIGNAL_H 1
|
|
|
|
|
#endif
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
|
|
|
|
/* Standard headers. */
|
2003-01-09 13:36:05 +01:00
|
|
|
|
#include <stdlib.h>
|
2011-09-27 17:18:56 +02:00
|
|
|
|
#include <stdio.h>
|
2003-01-09 13:36:05 +01:00
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <unistd.h>
|
2008-06-13 18:18:59 +02:00
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
2011-09-23 14:43:58 +02:00
|
|
|
|
# define WIN32_LEAN_AND_MEAN /* We only need the OS core stuff. */
|
2008-06-13 18:18:59 +02:00
|
|
|
|
# include <windows.h>
|
|
|
|
|
#else
|
2011-09-27 17:18:56 +02:00
|
|
|
|
# include <sys/types.h>
|
|
|
|
|
# include <sys/stat.h>
|
2008-06-13 18:18:59 +02:00
|
|
|
|
# include <sys/utsname.h>
|
2023-12-19 07:59:41 +01:00
|
|
|
|
# include <dirent.h>
|
2003-01-09 13:36:05 +01:00
|
|
|
|
#endif
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <fcntl.h>
|
2010-02-26 19:44:36 +01:00
|
|
|
|
#ifdef HAVE_SIGNAL_H
|
|
|
|
|
# include <signal.h>
|
|
|
|
|
#endif
|
2011-09-29 15:27:01 +02:00
|
|
|
|
#ifdef DOTLOCK_USE_PTHREAD
|
|
|
|
|
# include <pthread.h>
|
|
|
|
|
#endif
|
2003-01-09 13:36:05 +01:00
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
#ifdef DOTLOCK_GLIB_LOGGING
|
|
|
|
|
# include <glib.h>
|
|
|
|
|
#endif
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
#ifdef GNUPG_MAJOR_VERSION
|
2015-04-24 15:19:10 +02:00
|
|
|
|
# include "util.h"
|
|
|
|
|
# include "common-defs.h"
|
2014-03-07 09:46:44 +01:00
|
|
|
|
# include "stringhelp.h" /* For stpcpy and w32_strerror. */
|
2011-09-27 17:18:56 +02:00
|
|
|
|
#endif
|
2003-01-09 13:36:05 +01:00
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
#include "dotlock.h"
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
|
|
|
|
/* Define constants for file name construction. */
|
|
|
|
|
#if !defined(DIRSEP_C) && !defined(EXTSEP_S)
|
|
|
|
|
# ifdef HAVE_DOSISH_SYSTEM
|
|
|
|
|
# define DIRSEP_C '\\'
|
|
|
|
|
# define EXTSEP_S "."
|
2003-11-17 13:20:11 +01:00
|
|
|
|
#else
|
2011-09-23 14:43:58 +02:00
|
|
|
|
# define DIRSEP_C '/'
|
|
|
|
|
# define EXTSEP_S "."
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-04-28 03:06:33 +02:00
|
|
|
|
/* In GnuPG we use wrappers around the malloc functions. If they are
|
2011-09-23 14:43:58 +02:00
|
|
|
|
not defined we assume that this code is used outside of GnuPG and
|
|
|
|
|
fall back to the regular malloc functions. */
|
2015-04-24 15:19:10 +02:00
|
|
|
|
#ifndef xtrymalloc
|
|
|
|
|
# define xtrymalloc(a) malloc ((a))
|
|
|
|
|
# define xtrycalloc(a,b) calloc ((a), (b))
|
|
|
|
|
# define xfree(a) free ((a))
|
2003-11-17 13:20:11 +01:00
|
|
|
|
#endif
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
2015-04-24 15:19:10 +02:00
|
|
|
|
/* Wrapper to set ERRNO (required for W32CE). */
|
|
|
|
|
#ifdef GPG_ERROR_VERSION
|
|
|
|
|
# define my_set_errno(e) gpg_err_set_errno ((e))
|
|
|
|
|
#else
|
|
|
|
|
# define my_set_errno(e) do { errno = (e); } while (0)
|
2003-11-17 13:20:11 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
/* Gettext macro replacement. */
|
|
|
|
|
#ifndef _
|
|
|
|
|
# define _(a) (a)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef GNUPG_MAJOR_VERSION
|
|
|
|
|
# define my_info_0(a) log_info ((a))
|
|
|
|
|
# define my_info_1(a,b) log_info ((a), (b))
|
|
|
|
|
# define my_info_2(a,b,c) log_info ((a), (b), (c))
|
|
|
|
|
# define my_info_3(a,b,c,d) log_info ((a), (b), (c), (d))
|
|
|
|
|
# define my_error_0(a) log_error ((a))
|
|
|
|
|
# define my_error_1(a,b) log_error ((a), (b))
|
|
|
|
|
# define my_error_2(a,b,c) log_error ((a), (b), (c))
|
|
|
|
|
# define my_debug_1(a,b) log_debug ((a), (b))
|
2011-09-29 15:27:01 +02:00
|
|
|
|
# define my_fatal_0(a) log_fatal ((a))
|
2011-09-27 17:18:56 +02:00
|
|
|
|
#elif defined (DOTLOCK_GLIB_LOGGING)
|
|
|
|
|
# define my_info_0(a) g_message ((a))
|
|
|
|
|
# define my_info_1(a,b) g_message ((a), (b))
|
|
|
|
|
# define my_info_2(a,b,c) g_message ((a), (b), (c))
|
|
|
|
|
# define my_info_3(a,b,c,d) g_message ((a), (b), (c), (d))
|
|
|
|
|
# define my_error_0(a) g_warning ((a))
|
|
|
|
|
# define my_error_1(a,b) g_warning ((a), (b))
|
2011-09-30 09:45:21 +02:00
|
|
|
|
# define my_error_2(a,b,c) g_warning ((a), (b), (c))
|
2011-09-27 17:18:56 +02:00
|
|
|
|
# define my_debug_1(a,b) g_debug ((a), (b))
|
2011-09-29 15:27:01 +02:00
|
|
|
|
# define my_fatal_0(a) g_error ((a))
|
2011-09-27 17:18:56 +02:00
|
|
|
|
#else
|
|
|
|
|
# define my_info_0(a) fprintf (stderr, (a))
|
|
|
|
|
# define my_info_1(a,b) fprintf (stderr, (a), (b))
|
|
|
|
|
# define my_info_2(a,b,c) fprintf (stderr, (a), (b), (c))
|
|
|
|
|
# define my_info_3(a,b,c,d) fprintf (stderr, (a), (b), (c), (d))
|
|
|
|
|
# define my_error_0(a) fprintf (stderr, (a))
|
|
|
|
|
# define my_error_1(a,b) fprintf (stderr, (a), (b))
|
|
|
|
|
# define my_error_2(a,b,c) fprintf (stderr, (a), (b), (c))
|
|
|
|
|
# define my_debug_1(a,b) fprintf (stderr, (a), (b))
|
2011-09-29 15:27:01 +02:00
|
|
|
|
# define my_fatal_0(a) do { fprintf (stderr,(a)); fflush (stderr); \
|
|
|
|
|
abort (); } while (0)
|
2011-09-27 17:18:56 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-11-17 13:20:11 +01:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
2008-06-13 18:18:59 +02:00
|
|
|
|
/* The object describing a lock. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
struct dotlock_handle
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
|
|
|
|
struct dotlock_handle *next;
|
2011-09-27 17:18:56 +02:00
|
|
|
|
char *lockname; /* Name of the actual lockfile. */
|
|
|
|
|
unsigned int locked:1; /* Lock status. */
|
|
|
|
|
unsigned int disable:1; /* If true, locking is disabled. */
|
|
|
|
|
unsigned int use_o_excl:1; /* Use open (O_EXCL) for locking. */
|
2023-12-19 07:59:41 +01:00
|
|
|
|
unsigned int by_parent:1; /* Parent does the locking. */
|
|
|
|
|
unsigned int no_write:1; /* No write to the lockfile. */
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2011-09-29 16:51:48 +02:00
|
|
|
|
int extra_fd; /* A place for the caller to store an FD. */
|
|
|
|
|
|
2023-12-13 10:08:12 +01:00
|
|
|
|
/* An optional info callback - see dotlock_set_info_cb. */
|
|
|
|
|
int (*info_cb)(dotlock_t, void *,
|
|
|
|
|
enum dotlock_reasons reason,
|
|
|
|
|
const char *,...);
|
|
|
|
|
void *info_cb_value;
|
|
|
|
|
|
|
|
|
|
|
2008-06-13 18:18:59 +02:00
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
|
|
|
|
HANDLE lockhd; /* The W32 handle of the lock file. */
|
2011-09-22 14:27:32 +02:00
|
|
|
|
#else /*!HAVE_DOSISH_SYSTEM */
|
2008-06-13 18:18:59 +02:00
|
|
|
|
char *tname; /* Name of the lockfile template. */
|
2006-06-28 17:14:09 +02:00
|
|
|
|
size_t nodename_off; /* Offset in TNAME of the nodename part. */
|
2008-06-13 18:18:59 +02:00
|
|
|
|
size_t nodename_len; /* Length of the nodename part. */
|
2011-09-22 14:27:32 +02:00
|
|
|
|
#endif /*!HAVE_DOSISH_SYSTEM */
|
2003-01-09 13:36:05 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2017-02-20 22:19:50 +01:00
|
|
|
|
/* A list of all lock handles. The volatile attribute might help
|
2016-01-13 14:42:12 +01:00
|
|
|
|
if used in an atexit handler. Note that [UN]LOCK_all_lockfiles
|
|
|
|
|
must not change ERRNO. */
|
2009-09-23 12:28:41 +02:00
|
|
|
|
static volatile dotlock_t all_lockfiles;
|
2011-09-29 15:27:01 +02:00
|
|
|
|
#ifdef DOTLOCK_USE_PTHREAD
|
|
|
|
|
static pthread_mutex_t all_lockfiles_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
# define LOCK_all_lockfiles() do { \
|
|
|
|
|
if (pthread_mutex_lock (&all_lockfiles_mutex)) \
|
|
|
|
|
my_fatal_0 ("locking all_lockfiles_mutex failed\n"); \
|
|
|
|
|
} while (0)
|
|
|
|
|
# define UNLOCK_all_lockfiles() do { \
|
|
|
|
|
if (pthread_mutex_unlock (&all_lockfiles_mutex)) \
|
|
|
|
|
my_fatal_0 ("unlocking all_lockfiles_mutex failed\n"); \
|
|
|
|
|
} while (0)
|
|
|
|
|
#else /*!DOTLOCK_USE_PTHREAD*/
|
|
|
|
|
# define LOCK_all_lockfiles() do { } while (0)
|
|
|
|
|
# define UNLOCK_all_lockfiles() do { } while (0)
|
|
|
|
|
#endif /*!DOTLOCK_USE_PTHREAD*/
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
|
|
|
|
/* If this has the value true all locking is disabled. */
|
2003-06-18 21:56:13 +02:00
|
|
|
|
static int never_lock;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2011-09-29 15:27:01 +02:00
|
|
|
|
|
2016-01-13 14:42:12 +01:00
|
|
|
|
|
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
2021-01-11 14:19:06 +01:00
|
|
|
|
/* FIXME: For use in GnuPG this can be replaced by
|
|
|
|
|
* gnupg_w32_set_errno. */
|
2016-01-13 14:42:12 +01:00
|
|
|
|
static int
|
|
|
|
|
map_w32_to_errno (DWORD w32_err)
|
|
|
|
|
{
|
|
|
|
|
switch (w32_err)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
|
|
|
return ENOENT;
|
|
|
|
|
|
|
|
|
|
case ERROR_PATH_NOT_FOUND:
|
|
|
|
|
return ENOENT;
|
|
|
|
|
|
|
|
|
|
case ERROR_ACCESS_DENIED:
|
|
|
|
|
return EPERM;
|
|
|
|
|
|
|
|
|
|
case ERROR_INVALID_HANDLE:
|
|
|
|
|
case ERROR_INVALID_BLOCK:
|
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
|
|
|
return ENOMEM;
|
|
|
|
|
|
|
|
|
|
case ERROR_NO_DATA:
|
|
|
|
|
case ERROR_BROKEN_PIPE:
|
|
|
|
|
return EPIPE;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return EIO;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_DOSISH_SYSTEM*/
|
2011-09-29 15:27:01 +02:00
|
|
|
|
|
2020-10-20 13:38:11 +02:00
|
|
|
|
|
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
static int
|
|
|
|
|
any8bitchar (const char *string)
|
|
|
|
|
{
|
|
|
|
|
if (string)
|
|
|
|
|
for ( ; *string; string++)
|
|
|
|
|
if ((*string & 0x80))
|
|
|
|
|
return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_W32_SYSTEM*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
|
|
|
|
/* Entirely disable all locking. This function should be called
|
|
|
|
|
before any locking is done. It may be called right at startup of
|
|
|
|
|
the process as it only sets a global value. */
|
2003-06-18 21:56:13 +02:00
|
|
|
|
void
|
2011-09-23 14:43:58 +02:00
|
|
|
|
dotlock_disable (void)
|
2003-06-18 21:56:13 +02:00
|
|
|
|
{
|
2006-06-28 17:14:09 +02:00
|
|
|
|
never_lock = 1;
|
2003-06-18 21:56:13 +02:00
|
|
|
|
}
|
2003-01-09 13:36:05 +01:00
|
|
|
|
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
#ifdef HAVE_POSIX_SYSTEM
|
|
|
|
|
static int
|
|
|
|
|
maybe_deadlock (dotlock_t h)
|
|
|
|
|
{
|
|
|
|
|
dotlock_t r;
|
2011-09-29 15:27:01 +02:00
|
|
|
|
int res = 0;
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2011-09-29 15:27:01 +02:00
|
|
|
|
LOCK_all_lockfiles ();
|
|
|
|
|
for (r=all_lockfiles; r; r = r->next)
|
2011-09-23 14:43:58 +02:00
|
|
|
|
{
|
|
|
|
|
if ( r != h && r->locked )
|
2011-09-29 15:27:01 +02:00
|
|
|
|
{
|
|
|
|
|
res = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-09-23 14:43:58 +02:00
|
|
|
|
}
|
2011-09-29 15:27:01 +02:00
|
|
|
|
UNLOCK_all_lockfiles ();
|
|
|
|
|
return res;
|
2011-09-23 14:43:58 +02:00
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_POSIX_SYSTEM*/
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
/* Read the lock file and return the pid, returns -1 on error. True
|
|
|
|
|
will be stored in the integer at address SAME_NODE if the lock file
|
|
|
|
|
has been created on the same node. */
|
|
|
|
|
#ifdef HAVE_POSIX_SYSTEM
|
|
|
|
|
static int
|
2022-03-18 06:33:53 +01:00
|
|
|
|
read_lockfile (dotlock_t h, int *same_node, int *r_fd)
|
2003-01-09 13:36:05 +01:00
|
|
|
|
{
|
2011-09-23 14:43:58 +02:00
|
|
|
|
char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node
|
|
|
|
|
names are usually shorter. */
|
|
|
|
|
int fd;
|
|
|
|
|
int pid = -1;
|
|
|
|
|
char *buffer, *p;
|
|
|
|
|
size_t expected_len;
|
|
|
|
|
int res, nread;
|
2006-06-28 17:14:09 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
*same_node = 0;
|
|
|
|
|
expected_len = 10 + 1 + h->nodename_len + 1;
|
|
|
|
|
if ( expected_len >= sizeof buffer_space)
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2015-04-24 15:19:10 +02:00
|
|
|
|
buffer = xtrymalloc (expected_len);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
if (!buffer)
|
|
|
|
|
return -1;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
2011-09-23 14:43:58 +02:00
|
|
|
|
else
|
|
|
|
|
buffer = buffer_space;
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
if ( (fd = open (h->lockname, O_RDONLY)) == -1 )
|
|
|
|
|
{
|
|
|
|
|
int e = errno;
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (errno != ENOENT)
|
|
|
|
|
{
|
|
|
|
|
my_info_2 ("error opening lockfile '%s': %s\n",
|
|
|
|
|
h->lockname, strerror(errno) );
|
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
|
|
|
|
|
"error opening lockfile '%s': %s\n",
|
|
|
|
|
h->lockname, strerror (errno) );
|
|
|
|
|
}
|
2011-09-23 14:43:58 +02:00
|
|
|
|
if (buffer != buffer_space)
|
2015-04-24 15:19:10 +02:00
|
|
|
|
xfree (buffer);
|
|
|
|
|
my_set_errno (e); /* Need to return ERRNO here. */
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
2003-01-09 13:36:05 +01:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
p = buffer;
|
|
|
|
|
nread = 0;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
res = read (fd, p, expected_len - nread);
|
|
|
|
|
if (res == -1 && errno == EINTR)
|
|
|
|
|
continue;
|
|
|
|
|
if (res < 0)
|
|
|
|
|
{
|
2016-01-13 14:42:12 +01:00
|
|
|
|
int e = errno;
|
2012-06-05 19:29:22 +02:00
|
|
|
|
my_info_1 ("error reading lockfile '%s'\n", h->lockname );
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
|
|
|
|
|
"error reading lockfile '%s': %s\n",
|
|
|
|
|
h->lockname, strerror (errno) );
|
2011-09-23 14:43:58 +02:00
|
|
|
|
close (fd);
|
|
|
|
|
if (buffer != buffer_space)
|
2015-04-24 15:19:10 +02:00
|
|
|
|
xfree (buffer);
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (e);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
p += res;
|
|
|
|
|
nread += res;
|
|
|
|
|
}
|
|
|
|
|
while (res && nread != expected_len);
|
2022-03-18 06:33:53 +01:00
|
|
|
|
|
|
|
|
|
if (r_fd)
|
|
|
|
|
*r_fd = fd;
|
|
|
|
|
else
|
|
|
|
|
close(fd);
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
if (nread < 11)
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2012-06-05 19:29:22 +02:00
|
|
|
|
my_info_1 ("invalid size of lockfile '%s'\n", h->lockname);
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_INV_FILE,
|
|
|
|
|
"invalid size of lockfile '%s'\n", h->lockname);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
if (buffer != buffer_space)
|
2015-04-24 15:19:10 +02:00
|
|
|
|
xfree (buffer);
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (EINVAL);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return -1;
|
2003-06-18 21:56:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
if (buffer[10] != '\n'
|
|
|
|
|
|| (buffer[10] = 0, pid = atoi (buffer)) == -1
|
|
|
|
|
|| !pid )
|
|
|
|
|
{
|
2012-06-05 19:29:22 +02:00
|
|
|
|
my_error_2 ("invalid pid %d in lockfile '%s'\n", pid, h->lockname);
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_INV_FILE,
|
|
|
|
|
"invalid pid %d in lockfile '%s'\n", pid, h->lockname);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
if (buffer != buffer_space)
|
2015-04-24 15:19:10 +02:00
|
|
|
|
xfree (buffer);
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (EINVAL);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nread == expected_len
|
|
|
|
|
&& !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len)
|
|
|
|
|
&& buffer[11+h->nodename_len] == '\n')
|
|
|
|
|
*same_node = 1;
|
|
|
|
|
|
|
|
|
|
if (buffer != buffer_space)
|
2015-04-24 15:19:10 +02:00
|
|
|
|
xfree (buffer);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return pid;
|
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_POSIX_SYSTEM */
|
|
|
|
|
|
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
/* Check whether the file system which stores TNAME supports
|
|
|
|
|
hardlinks. Instead of using the non-portable statsfs call which
|
|
|
|
|
differs between various Unix versions, we do a runtime test.
|
|
|
|
|
Returns: 0 supports hardlinks; 1 no hardlink support, -1 unknown
|
|
|
|
|
(test error). */
|
|
|
|
|
#ifdef HAVE_POSIX_SYSTEM
|
|
|
|
|
static int
|
|
|
|
|
use_hardlinks_p (const char *tname)
|
|
|
|
|
{
|
|
|
|
|
char *lname;
|
|
|
|
|
struct stat sb;
|
|
|
|
|
unsigned int nlink;
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
if (stat (tname, &sb))
|
|
|
|
|
return -1;
|
|
|
|
|
nlink = (unsigned int)sb.st_nlink;
|
|
|
|
|
|
2015-04-24 15:19:10 +02:00
|
|
|
|
lname = xtrymalloc (strlen (tname) + 1 + 1);
|
2011-09-27 17:18:56 +02:00
|
|
|
|
if (!lname)
|
|
|
|
|
return -1;
|
|
|
|
|
strcpy (lname, tname);
|
|
|
|
|
strcat (lname, "x");
|
|
|
|
|
|
2012-01-03 17:32:41 +01:00
|
|
|
|
/* We ignore the return value of link() because it is unreliable. */
|
|
|
|
|
(void) link (tname, lname);
|
2012-01-03 17:08:01 +01:00
|
|
|
|
|
2012-01-03 17:32:41 +01:00
|
|
|
|
if (stat (tname, &sb))
|
|
|
|
|
res = -1; /* Ooops. */
|
|
|
|
|
else if (sb.st_nlink == nlink + 1)
|
|
|
|
|
res = 0; /* Yeah, hardlinks are supported. */
|
|
|
|
|
else
|
|
|
|
|
res = 1; /* No hardlink support. */
|
2011-09-27 17:18:56 +02:00
|
|
|
|
|
2012-01-03 17:32:41 +01:00
|
|
|
|
unlink (lname);
|
2015-04-24 15:19:10 +02:00
|
|
|
|
xfree (lname);
|
2011-09-27 17:18:56 +02:00
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_POSIX_SYSTEM */
|
|
|
|
|
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
|
|
|
|
#ifdef HAVE_POSIX_SYSTEM
|
2023-12-19 07:59:41 +01:00
|
|
|
|
static int
|
|
|
|
|
dotlock_get_process_id (dotlock_t h)
|
|
|
|
|
{
|
|
|
|
|
return h->by_parent? (int)getppid(): (int)getpid();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
dotlock_detect_tname (dotlock_t h)
|
|
|
|
|
{
|
|
|
|
|
struct stat sb;
|
|
|
|
|
DIR *dir;
|
|
|
|
|
char *dirname;
|
|
|
|
|
char *basename;
|
|
|
|
|
struct dirent *d;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
if (stat (h->lockname, &sb))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
basename = make_basename (h->lockname, NULL);
|
|
|
|
|
dirname = make_dirname (h->lockname);
|
|
|
|
|
|
|
|
|
|
dir = opendir (dirname);
|
|
|
|
|
if (dir == NULL)
|
|
|
|
|
{
|
|
|
|
|
xfree (basename);
|
|
|
|
|
xfree (dirname);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while ((d = readdir (dir)))
|
|
|
|
|
if (sb.st_ino == d->d_ino && strcmp (d->d_name, basename))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (d)
|
|
|
|
|
{
|
|
|
|
|
int len = strlen (h->tname);
|
|
|
|
|
int dlen = strlen (d->d_name);
|
|
|
|
|
const char *tname_path;
|
|
|
|
|
|
|
|
|
|
if (dlen > len)
|
|
|
|
|
{
|
|
|
|
|
xfree (basename);
|
|
|
|
|
xfree (dirname);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
strcpy (stpcpy (stpcpy (h->tname, dirname), DIRSEP_S), d->d_name);
|
|
|
|
|
h->use_o_excl = 0;
|
|
|
|
|
tname_path = strchr (h->tname + strlen (dirname) + 2, '.');
|
|
|
|
|
if (!tname_path)
|
|
|
|
|
{
|
|
|
|
|
xfree (basename);
|
|
|
|
|
xfree (dirname);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
h->nodename_off = tname_path - h->tname + 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
h->use_o_excl = 1;
|
|
|
|
|
|
|
|
|
|
r = closedir (dir);
|
|
|
|
|
if (r)
|
|
|
|
|
{
|
|
|
|
|
xfree (basename);
|
|
|
|
|
xfree (dirname);
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xfree (basename);
|
|
|
|
|
xfree (dirname);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
/* Locking core for Unix. It used a temporary file and the link
|
|
|
|
|
system call to make locking an atomic operation. */
|
|
|
|
|
static dotlock_t
|
|
|
|
|
dotlock_create_unix (dotlock_t h, const char *file_to_lock)
|
|
|
|
|
{
|
|
|
|
|
int fd = -1;
|
|
|
|
|
char pidstr[16];
|
|
|
|
|
const char *nodename;
|
|
|
|
|
const char *dirpart;
|
|
|
|
|
int dirpartlen;
|
|
|
|
|
struct utsname utsbuf;
|
|
|
|
|
size_t tnamelen;
|
2023-12-19 07:59:41 +01:00
|
|
|
|
int pid;
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2023-12-19 07:59:41 +01:00
|
|
|
|
pid = dotlock_get_process_id (h);
|
|
|
|
|
snprintf (pidstr, sizeof pidstr, "%10d\n", pid);
|
2006-06-28 17:14:09 +02:00
|
|
|
|
|
|
|
|
|
/* Create a temporary file. */
|
|
|
|
|
if ( uname ( &utsbuf ) )
|
|
|
|
|
nodename = "unknown";
|
|
|
|
|
else
|
|
|
|
|
nodename = utsbuf.nodename;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2008-06-13 18:18:59 +02:00
|
|
|
|
if ( !(dirpart = strrchr (file_to_lock, DIRSEP_C)) )
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
|
|
|
|
dirpart = EXTSEP_S;
|
|
|
|
|
dirpartlen = 1;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
2006-06-28 17:14:09 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dirpartlen = dirpart - file_to_lock;
|
|
|
|
|
dirpart = file_to_lock;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
2011-09-29 15:27:01 +02:00
|
|
|
|
LOCK_all_lockfiles ();
|
2006-06-28 17:14:09 +02:00
|
|
|
|
h->next = all_lockfiles;
|
|
|
|
|
all_lockfiles = h;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10 + 1;
|
2015-04-24 15:19:10 +02:00
|
|
|
|
h->tname = xtrymalloc (tnamelen + 1);
|
2008-06-13 18:18:59 +02:00
|
|
|
|
if (!h->tname)
|
|
|
|
|
{
|
|
|
|
|
all_lockfiles = h->next;
|
2011-09-29 15:27:01 +02:00
|
|
|
|
UNLOCK_all_lockfiles ();
|
2015-04-24 15:19:10 +02:00
|
|
|
|
xfree (h);
|
2008-06-13 18:18:59 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2006-06-28 17:14:09 +02:00
|
|
|
|
h->nodename_len = strlen (nodename);
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2023-12-19 07:59:41 +01:00
|
|
|
|
if (h->no_write)
|
|
|
|
|
{
|
|
|
|
|
memset (h->tname, '_', tnamelen);
|
|
|
|
|
h->tname[tnamelen] = 0;
|
|
|
|
|
goto skip_write;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-13 18:18:59 +02:00
|
|
|
|
snprintf (h->tname, tnamelen, "%.*s/.#lk%p.", dirpartlen, dirpart, h );
|
2006-06-28 17:14:09 +02:00
|
|
|
|
h->nodename_off = strlen (h->tname);
|
2008-06-13 18:18:59 +02:00
|
|
|
|
snprintf (h->tname+h->nodename_off, tnamelen - h->nodename_off,
|
2023-12-19 07:59:41 +01:00
|
|
|
|
"%s.%d", nodename, pid);
|
2003-01-09 13:36:05 +01:00
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
do
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2015-04-24 15:19:10 +02:00
|
|
|
|
my_set_errno (0);
|
2006-06-28 17:14:09 +02:00
|
|
|
|
fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL,
|
|
|
|
|
S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
|
2011-02-04 12:57:53 +01:00
|
|
|
|
}
|
2006-06-28 17:14:09 +02:00
|
|
|
|
while (fd == -1 && errno == EINTR);
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
if ( fd == -1 )
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2016-01-13 14:42:12 +01:00
|
|
|
|
int saveerrno = errno;
|
2006-06-28 17:14:09 +02:00
|
|
|
|
all_lockfiles = h->next;
|
2011-09-29 15:27:01 +02:00
|
|
|
|
UNLOCK_all_lockfiles ();
|
2012-06-05 19:29:22 +02:00
|
|
|
|
my_error_2 (_("failed to create temporary file '%s': %s\n"),
|
2016-01-13 14:42:12 +01:00
|
|
|
|
h->tname, strerror (errno));
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_WAITING,
|
|
|
|
|
_("failed to create temporary file '%s': %s\n"),
|
|
|
|
|
h->tname, strerror (errno));
|
2015-04-24 15:19:10 +02:00
|
|
|
|
xfree (h->tname);
|
|
|
|
|
xfree (h);
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (saveerrno);
|
2006-06-28 17:14:09 +02:00
|
|
|
|
return NULL;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
2006-06-28 17:14:09 +02:00
|
|
|
|
if ( write (fd, pidstr, 11 ) != 11 )
|
|
|
|
|
goto write_failed;
|
|
|
|
|
if ( write (fd, nodename, strlen (nodename) ) != strlen (nodename) )
|
|
|
|
|
goto write_failed;
|
|
|
|
|
if ( write (fd, "\n", 1 ) != 1 )
|
|
|
|
|
goto write_failed;
|
|
|
|
|
if ( close (fd) )
|
2014-12-19 23:12:37 +01:00
|
|
|
|
{
|
|
|
|
|
if ( errno == EINTR )
|
|
|
|
|
fd = -1;
|
|
|
|
|
goto write_failed;
|
|
|
|
|
}
|
|
|
|
|
fd = -1;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
/* Check whether we support hard links. */
|
|
|
|
|
switch (use_hardlinks_p (h->tname))
|
|
|
|
|
{
|
|
|
|
|
case 0: /* Yes. */
|
|
|
|
|
break;
|
|
|
|
|
case 1: /* No. */
|
|
|
|
|
unlink (h->tname);
|
|
|
|
|
h->use_o_excl = 1;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2016-01-13 14:42:12 +01:00
|
|
|
|
{
|
|
|
|
|
int saveerrno = errno;
|
|
|
|
|
my_error_2 ("can't check whether hardlinks are supported for '%s': %s\n"
|
|
|
|
|
, h->tname, strerror (saveerrno));
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_CONFIG_TEST,
|
|
|
|
|
"can't check whether hardlinks are supported for '%s': %s\n"
|
|
|
|
|
, h->tname, strerror (saveerrno));
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (saveerrno);
|
|
|
|
|
}
|
2011-09-27 17:18:56 +02:00
|
|
|
|
goto write_failed;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-19 07:59:41 +01:00
|
|
|
|
skip_write:
|
2015-04-24 15:19:10 +02:00
|
|
|
|
h->lockname = xtrymalloc (strlen (file_to_lock) + 6 );
|
2008-06-13 18:18:59 +02:00
|
|
|
|
if (!h->lockname)
|
|
|
|
|
{
|
2016-01-13 14:42:12 +01:00
|
|
|
|
int saveerrno = errno;
|
2008-06-13 18:18:59 +02:00
|
|
|
|
all_lockfiles = h->next;
|
2011-09-29 15:27:01 +02:00
|
|
|
|
UNLOCK_all_lockfiles ();
|
2008-06-13 18:18:59 +02:00
|
|
|
|
unlink (h->tname);
|
2015-04-24 15:19:10 +02:00
|
|
|
|
xfree (h->tname);
|
|
|
|
|
xfree (h);
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (saveerrno);
|
2008-06-13 18:18:59 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock");
|
2011-09-29 15:27:01 +02:00
|
|
|
|
UNLOCK_all_lockfiles ();
|
2011-09-27 17:18:56 +02:00
|
|
|
|
|
2023-12-19 07:59:41 +01:00
|
|
|
|
if (h->no_write)
|
|
|
|
|
{
|
|
|
|
|
if (dotlock_detect_tname (h) < 0)
|
|
|
|
|
{
|
|
|
|
|
xfree (h->lockname);
|
|
|
|
|
xfree (h->tname);
|
|
|
|
|
xfree (h);
|
|
|
|
|
my_set_errno (EACCES);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h->locked = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-28 17:14:09 +02:00
|
|
|
|
return h;
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2006-06-28 17:14:09 +02:00
|
|
|
|
write_failed:
|
2016-01-13 14:42:12 +01:00
|
|
|
|
{
|
|
|
|
|
int saveerrno = errno;
|
|
|
|
|
all_lockfiles = h->next;
|
|
|
|
|
UNLOCK_all_lockfiles ();
|
|
|
|
|
my_error_2 (_("error writing to '%s': %s\n"), h->tname, strerror (errno));
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
|
|
|
|
|
_("error writing to '%s': %s\n"),
|
|
|
|
|
h->tname, strerror (errno));
|
|
|
|
|
|
2016-01-13 14:42:12 +01:00
|
|
|
|
if ( fd != -1 )
|
|
|
|
|
close (fd);
|
|
|
|
|
unlink (h->tname);
|
|
|
|
|
xfree (h->tname);
|
|
|
|
|
xfree (h);
|
|
|
|
|
my_set_errno (saveerrno);
|
|
|
|
|
}
|
2006-06-28 17:14:09 +02:00
|
|
|
|
return NULL;
|
2011-09-23 14:43:58 +02:00
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_POSIX_SYSTEM*/
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
|
|
|
|
/* Locking core for Windows. This version does not need a temporary
|
|
|
|
|
file but uses the plain lock file along with record locking. We
|
|
|
|
|
create this file here so that we later only need to do the file
|
|
|
|
|
locking. For error reporting it is useful to keep the name of the
|
|
|
|
|
file in the handle. */
|
|
|
|
|
static dotlock_t
|
|
|
|
|
dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
|
|
|
|
|
{
|
2011-09-29 15:27:01 +02:00
|
|
|
|
LOCK_all_lockfiles ();
|
2008-06-13 18:18:59 +02:00
|
|
|
|
h->next = all_lockfiles;
|
|
|
|
|
all_lockfiles = h;
|
|
|
|
|
|
2020-10-20 13:38:11 +02:00
|
|
|
|
h->lockname = strconcat (file_to_lock, EXTSEP_S "lock", NULL);
|
2008-06-13 18:18:59 +02:00
|
|
|
|
if (!h->lockname)
|
|
|
|
|
{
|
|
|
|
|
all_lockfiles = h->next;
|
2011-09-29 15:27:01 +02:00
|
|
|
|
UNLOCK_all_lockfiles ();
|
2015-04-24 15:19:10 +02:00
|
|
|
|
xfree (h);
|
2008-06-13 18:18:59 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE
|
|
|
|
|
along with FILE_SHARE_DELETE but that does not work due to a race
|
|
|
|
|
condition: Despite the OPEN_ALWAYS flag CreateFile may return an
|
|
|
|
|
error and we can't reliable create/open the lock file unless we
|
|
|
|
|
would wait here until it works - however there are other valid
|
|
|
|
|
reasons why a lock file can't be created and thus the process
|
2011-09-23 14:43:58 +02:00
|
|
|
|
would not stop as expected but spin until Windows crashes. Our
|
|
|
|
|
solution is to keep the lock file open; that does not harm. */
|
2020-10-20 13:38:11 +02:00
|
|
|
|
if (any8bitchar (h->lockname))
|
|
|
|
|
{
|
|
|
|
|
wchar_t *wname = utf8_to_wchar (h->lockname);
|
|
|
|
|
|
|
|
|
|
if (wname)
|
|
|
|
|
h->lockhd = CreateFileW (wname,
|
|
|
|
|
GENERIC_READ|GENERIC_WRITE,
|
|
|
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
|
|
|
NULL, OPEN_ALWAYS, 0, NULL);
|
|
|
|
|
else
|
|
|
|
|
h->lockhd = INVALID_HANDLE_VALUE;
|
|
|
|
|
xfree (wname);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
h->lockhd = CreateFileA (h->lockname,
|
|
|
|
|
GENERIC_READ|GENERIC_WRITE,
|
|
|
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
|
|
|
NULL, OPEN_ALWAYS, 0, NULL);
|
2008-06-13 18:18:59 +02:00
|
|
|
|
if (h->lockhd == INVALID_HANDLE_VALUE)
|
|
|
|
|
{
|
2016-01-13 14:42:12 +01:00
|
|
|
|
int saveerrno = map_w32_to_errno (GetLastError ());
|
2008-06-13 18:18:59 +02:00
|
|
|
|
all_lockfiles = h->next;
|
2011-09-29 15:27:01 +02:00
|
|
|
|
UNLOCK_all_lockfiles ();
|
2012-06-05 19:29:22 +02:00
|
|
|
|
my_error_2 (_("can't create '%s': %s\n"), h->lockname, w32_strerror (-1));
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
|
|
|
|
|
_("can't create '%s': %s\n"),
|
|
|
|
|
h->lockname, w32_strerror (-1));
|
2015-04-24 15:19:10 +02:00
|
|
|
|
xfree (h->lockname);
|
|
|
|
|
xfree (h);
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (saveerrno);
|
2008-06-13 18:18:59 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return h;
|
2011-09-23 14:43:58 +02:00
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_DOSISH_SYSTEM*/
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
|
|
|
|
/* Create a lockfile for a file name FILE_TO_LOCK and returns an
|
|
|
|
|
object of type dotlock_t which may be used later to actually acquire
|
|
|
|
|
the lock. A cleanup routine gets installed to cleanup left over
|
|
|
|
|
locks or other files used internally by the lock mechanism.
|
|
|
|
|
|
|
|
|
|
Calling this function with NULL does only install the atexit
|
|
|
|
|
handler and may thus be used to assure that the cleanup is called
|
|
|
|
|
after all other atexit handlers.
|
|
|
|
|
|
|
|
|
|
This function creates a lock file in the same directory as
|
|
|
|
|
FILE_TO_LOCK using that name and a suffix of ".lock". Note that on
|
|
|
|
|
POSIX systems a temporary file ".#lk.<hostname>.pid[.threadid] is
|
|
|
|
|
used.
|
|
|
|
|
|
2023-12-19 07:59:41 +01:00
|
|
|
|
FLAGS may include DOTLOCK_PREPARE_CREATE bit, which only allocates
|
|
|
|
|
the handle and requires a further call to dotlock_finish_create.
|
|
|
|
|
This can be used to set a callback between these calls.
|
|
|
|
|
|
|
|
|
|
FLAGS may include DOTLOCK_LOCK_BY_PARENT bit, when it's the parent
|
|
|
|
|
process controlling the lock. This is used by dotlock util.
|
|
|
|
|
|
|
|
|
|
FLAGS may include DOTLOCK_LOCKED bit, when it should not create the
|
|
|
|
|
lockfile, but to unlock. This is used by dotlock util.
|
2011-09-28 15:41:58 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
The function returns an new handle which needs to be released using
|
|
|
|
|
destroy_dotlock but gets also released at the termination of the
|
|
|
|
|
process. On error NULL is returned.
|
|
|
|
|
*/
|
2011-09-28 15:41:58 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
dotlock_t
|
2011-09-28 15:41:58 +02:00
|
|
|
|
dotlock_create (const char *file_to_lock, unsigned int flags)
|
2011-09-23 14:43:58 +02:00
|
|
|
|
{
|
|
|
|
|
static int initialized;
|
|
|
|
|
dotlock_t h;
|
2023-12-19 07:59:41 +01:00
|
|
|
|
#ifndef HAVE_DOSISH_SYSTEM
|
|
|
|
|
int by_parent = 0;
|
|
|
|
|
int no_write = 0;
|
|
|
|
|
#endif
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
2023-12-19 07:59:41 +01:00
|
|
|
|
if ( !(flags & DOTLOCK_LOCK_BY_PARENT)
|
|
|
|
|
&& !initialized )
|
2011-09-23 14:43:58 +02:00
|
|
|
|
{
|
|
|
|
|
atexit (dotlock_remove_lockfiles);
|
|
|
|
|
initialized = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !file_to_lock )
|
|
|
|
|
return NULL; /* Only initialization was requested. */
|
|
|
|
|
|
2023-12-19 07:59:41 +01:00
|
|
|
|
#ifndef HAVE_DOSISH_SYSTEM
|
|
|
|
|
if ((flags & DOTLOCK_LOCK_BY_PARENT) || (flags & DOTLOCK_LOCKED))
|
|
|
|
|
{
|
|
|
|
|
by_parent = !!(flags & DOTLOCK_LOCK_BY_PARENT);
|
|
|
|
|
no_write = !!(flags & DOTLOCK_LOCKED);
|
|
|
|
|
flags &= ~(DOTLOCK_LOCK_BY_PARENT | DOTLOCK_LOCKED);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if ((flags & ~DOTLOCK_PREPARE_CREATE))
|
2011-09-28 15:41:58 +02:00
|
|
|
|
{
|
2015-04-24 15:19:10 +02:00
|
|
|
|
my_set_errno (EINVAL);
|
2011-09-28 15:41:58 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-24 15:19:10 +02:00
|
|
|
|
h = xtrycalloc (1, sizeof *h);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
if (!h)
|
|
|
|
|
return NULL;
|
2011-09-29 16:51:48 +02:00
|
|
|
|
h->extra_fd = -1;
|
2023-12-19 07:59:41 +01:00
|
|
|
|
#ifndef HAVE_DOSISH_SYSTEM
|
|
|
|
|
h->by_parent = by_parent;
|
|
|
|
|
h->no_write = no_write;
|
|
|
|
|
#endif
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
|
|
|
|
if (never_lock)
|
|
|
|
|
{
|
|
|
|
|
h->disable = 1;
|
2011-09-29 15:27:01 +02:00
|
|
|
|
LOCK_all_lockfiles ();
|
2011-09-23 14:43:58 +02:00
|
|
|
|
h->next = all_lockfiles;
|
|
|
|
|
all_lockfiles = h;
|
2011-09-29 15:27:01 +02:00
|
|
|
|
UNLOCK_all_lockfiles ();
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return h;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if ((flags & DOTLOCK_PREPARE_CREATE))
|
|
|
|
|
return h;
|
|
|
|
|
else
|
|
|
|
|
return dotlock_finish_create (h, file_to_lock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This function may be used along with dotlock_create (file_name,
|
|
|
|
|
* DOTLOCK_PREPARE_CREATE) to finish the creation call. The given
|
|
|
|
|
* filename shall be the same as passed to dotlock_create. On success
|
|
|
|
|
* the same handle H is returned, on error NULL is returned and H is
|
|
|
|
|
* released. */
|
|
|
|
|
dotlock_t
|
|
|
|
|
dotlock_finish_create (dotlock_t h, const char *file_to_lock)
|
|
|
|
|
{
|
|
|
|
|
if (!h || !file_to_lock)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
|
|
|
|
return dotlock_create_w32 (h, file_to_lock);
|
|
|
|
|
#else /*!HAVE_DOSISH_SYSTEM */
|
|
|
|
|
return dotlock_create_unix (h, file_to_lock);
|
|
|
|
|
#endif /*!HAVE_DOSISH_SYSTEM*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-09-29 16:51:48 +02:00
|
|
|
|
|
2017-02-20 22:19:50 +01:00
|
|
|
|
/* Convenience function to store a file descriptor (or any other
|
2011-09-29 16:51:48 +02:00
|
|
|
|
integer value) in the context of handle H. */
|
|
|
|
|
void
|
|
|
|
|
dotlock_set_fd (dotlock_t h, int fd)
|
|
|
|
|
{
|
|
|
|
|
h->extra_fd = fd;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-20 22:19:50 +01:00
|
|
|
|
/* Convenience function to retrieve a file descriptor (or any other
|
2011-09-29 16:51:48 +02:00
|
|
|
|
integer value) stored in the context of handle H. */
|
|
|
|
|
int
|
|
|
|
|
dotlock_get_fd (dotlock_t h)
|
|
|
|
|
{
|
|
|
|
|
return h->extra_fd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-12-13 10:08:12 +01:00
|
|
|
|
/* Set a callback function for info diagnostics. The callback
|
|
|
|
|
* function CB is called with the handle, the opaque value OPAQUE, a
|
|
|
|
|
* reason code, and a format string with its arguments. The callback
|
|
|
|
|
* shall return 0 to continue operation or true in which case the
|
|
|
|
|
* current function will be terminated with an error. */
|
|
|
|
|
void
|
|
|
|
|
dotlock_set_info_cb (dotlock_t h,
|
|
|
|
|
int (*cb)(dotlock_t, void *,
|
|
|
|
|
enum dotlock_reasons reason,
|
|
|
|
|
const char *,...),
|
|
|
|
|
void *opaque)
|
|
|
|
|
{
|
|
|
|
|
h->info_cb = cb;
|
|
|
|
|
h->info_cb_value = opaque;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
|
|
|
|
#ifdef HAVE_POSIX_SYSTEM
|
|
|
|
|
/* Unix specific code of destroy_dotlock. */
|
|
|
|
|
static void
|
|
|
|
|
dotlock_destroy_unix (dotlock_t h)
|
|
|
|
|
{
|
|
|
|
|
if (h->locked && h->lockname)
|
|
|
|
|
unlink (h->lockname);
|
2011-09-27 17:18:56 +02:00
|
|
|
|
if (h->tname && !h->use_o_excl)
|
2011-09-23 14:43:58 +02:00
|
|
|
|
unlink (h->tname);
|
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_POSIX_SYSTEM*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
|
|
|
|
/* Windows specific code of destroy_dotlock. */
|
|
|
|
|
static void
|
|
|
|
|
dotlock_destroy_w32 (dotlock_t h)
|
|
|
|
|
{
|
|
|
|
|
if (h->locked)
|
|
|
|
|
{
|
|
|
|
|
OVERLAPPED ovl;
|
|
|
|
|
|
|
|
|
|
memset (&ovl, 0, sizeof ovl);
|
|
|
|
|
UnlockFileEx (h->lockhd, 0, 1, 0, &ovl);
|
|
|
|
|
}
|
|
|
|
|
CloseHandle (h->lockhd);
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
2011-09-23 14:43:58 +02:00
|
|
|
|
#endif /*HAVE_DOSISH_SYSTEM*/
|
2003-01-09 13:36:05 +01:00
|
|
|
|
|
2006-05-23 18:19:43 +02:00
|
|
|
|
|
2016-01-13 14:42:12 +01:00
|
|
|
|
/* Destroy the lock handle H and release the lock. */
|
2006-05-23 18:19:43 +02:00
|
|
|
|
void
|
2011-09-23 14:43:58 +02:00
|
|
|
|
dotlock_destroy (dotlock_t h)
|
2006-05-23 18:19:43 +02:00
|
|
|
|
{
|
2009-09-23 12:28:41 +02:00
|
|
|
|
dotlock_t hprev, htmp;
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
|
|
|
|
if ( !h )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* First remove the handle from our global list of all locks. */
|
2011-09-29 15:27:01 +02:00
|
|
|
|
LOCK_all_lockfiles ();
|
2008-06-13 18:18:59 +02:00
|
|
|
|
for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
|
|
|
|
|
if (htmp == h)
|
|
|
|
|
{
|
|
|
|
|
if (hprev)
|
|
|
|
|
hprev->next = htmp->next;
|
|
|
|
|
else
|
|
|
|
|
all_lockfiles = htmp->next;
|
|
|
|
|
h->next = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-09-29 15:27:01 +02:00
|
|
|
|
UNLOCK_all_lockfiles ();
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2008-06-13 18:18:59 +02:00
|
|
|
|
/* Then destroy the lock. */
|
2023-12-20 02:38:08 +01:00
|
|
|
|
if (!h->disable
|
|
|
|
|
&& (!h->by_parent || h->no_write))
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2023-12-20 02:38:08 +01:00
|
|
|
|
/* NOTE: under the condition of (by_parent && !no_write),
|
|
|
|
|
it doesn't come here. So, the lock file remains. */
|
2008-06-13 18:18:59 +02:00
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
2011-09-23 14:43:58 +02:00
|
|
|
|
dotlock_destroy_w32 (h);
|
2008-06-13 18:18:59 +02:00
|
|
|
|
#else /* !HAVE_DOSISH_SYSTEM */
|
2011-09-23 14:43:58 +02:00
|
|
|
|
dotlock_destroy_unix (h);
|
2008-06-13 18:18:59 +02:00
|
|
|
|
#endif /* HAVE_DOSISH_SYSTEM */
|
2006-06-28 17:14:09 +02:00
|
|
|
|
}
|
2023-12-20 02:28:02 +01:00
|
|
|
|
|
|
|
|
|
#ifdef HAVE_POSIX_SYSTEM
|
2023-12-20 02:53:50 +01:00
|
|
|
|
/* When DOTLOCK_LOCK_BY_PARENT and lock fails,
|
|
|
|
|
the temporary file created should be removed. */
|
|
|
|
|
if (h->by_parent && !h->no_write && !h->locked)
|
|
|
|
|
if (h->tname && !h->use_o_excl)
|
|
|
|
|
unlink (h->tname);
|
|
|
|
|
|
2023-12-20 02:28:02 +01:00
|
|
|
|
xfree (h->tname);
|
|
|
|
|
#endif
|
|
|
|
|
xfree (h->lockname);
|
2015-04-24 15:19:10 +02:00
|
|
|
|
xfree(h);
|
2006-05-23 18:19:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-10-02 12:53:41 +02:00
|
|
|
|
/* Return true if H has been taken. */
|
|
|
|
|
int
|
|
|
|
|
dotlock_is_locked (dotlock_t h)
|
|
|
|
|
{
|
|
|
|
|
return h && !!h->locked;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return the next interval to wait. WTIME and TIMEOUT are pointers
|
|
|
|
|
* to the current state and are updated by this function. The
|
|
|
|
|
* returned value might be different from the value of WTIME. */
|
|
|
|
|
static int
|
|
|
|
|
next_wait_interval (int *wtime, long *timeout)
|
|
|
|
|
{
|
|
|
|
|
int result;
|
|
|
|
|
|
|
|
|
|
/* Wait until lock has been released. We use retry intervals of 4,
|
|
|
|
|
* 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 512, 1024, 2048ms, and
|
|
|
|
|
* so on. If wait-forever was requested we add a small random value
|
|
|
|
|
* to have different timeouts per process. */
|
|
|
|
|
if (!*wtime)
|
|
|
|
|
*wtime = 4;
|
|
|
|
|
else if (*wtime < 2048)
|
|
|
|
|
*wtime *= 2;
|
|
|
|
|
else
|
|
|
|
|
*wtime = 512;
|
|
|
|
|
|
|
|
|
|
result = *wtime;
|
|
|
|
|
if (*wtime > 8 && *timeout < 0)
|
|
|
|
|
result += ((unsigned int)getpid() % 37);
|
|
|
|
|
|
|
|
|
|
if (*timeout > 0)
|
|
|
|
|
{
|
|
|
|
|
if (result > *timeout)
|
|
|
|
|
result = *timeout;
|
|
|
|
|
*timeout -= result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
|
|
|
|
#ifdef HAVE_POSIX_SYSTEM
|
2011-09-28 11:47:40 +02:00
|
|
|
|
/* Unix specific code of make_dotlock. Returns 0 on success and -1 on
|
|
|
|
|
error. */
|
2003-01-09 13:36:05 +01:00
|
|
|
|
static int
|
2011-09-28 11:47:40 +02:00
|
|
|
|
dotlock_take_unix (dotlock_t h, long timeout)
|
2003-01-09 13:36:05 +01:00
|
|
|
|
{
|
2011-09-28 11:47:40 +02:00
|
|
|
|
int wtime = 0;
|
2023-12-12 16:31:30 +01:00
|
|
|
|
int timedout = 0;
|
2011-09-28 11:47:40 +02:00
|
|
|
|
int sumtime = 0;
|
|
|
|
|
int pid;
|
|
|
|
|
int lastpid = -1;
|
|
|
|
|
int ownerchanged;
|
2006-06-28 17:14:09 +02:00
|
|
|
|
const char *maybe_dead="";
|
|
|
|
|
int same_node;
|
2016-01-13 14:42:12 +01:00
|
|
|
|
int saveerrno;
|
2022-03-18 06:33:53 +01:00
|
|
|
|
int fd;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
|
2011-09-28 11:47:40 +02:00
|
|
|
|
again:
|
2011-09-27 17:18:56 +02:00
|
|
|
|
if (h->use_o_excl)
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2011-09-27 17:18:56 +02:00
|
|
|
|
/* No hardlink support - use open(O_EXCL). */
|
|
|
|
|
do
|
|
|
|
|
{
|
2015-04-24 15:19:10 +02:00
|
|
|
|
my_set_errno (0);
|
2011-09-27 17:18:56 +02:00
|
|
|
|
fd = open (h->lockname, O_WRONLY|O_CREAT|O_EXCL,
|
|
|
|
|
S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
|
|
|
|
|
}
|
|
|
|
|
while (fd == -1 && errno == EINTR);
|
|
|
|
|
|
|
|
|
|
if (fd == -1 && errno == EEXIST)
|
|
|
|
|
; /* Lock held by another process. */
|
|
|
|
|
else if (fd == -1)
|
|
|
|
|
{
|
2016-01-13 14:42:12 +01:00
|
|
|
|
saveerrno = errno;
|
2012-06-05 19:29:22 +02:00
|
|
|
|
my_error_2 ("lock not made: open(O_EXCL) of '%s' failed: %s\n",
|
2016-01-13 14:42:12 +01:00
|
|
|
|
h->lockname, strerror (saveerrno));
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
|
|
|
|
|
"lock not made: open(O_EXCL) of '%s' failed: %s\n",
|
|
|
|
|
h->lockname, strerror (saveerrno));
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (saveerrno);
|
2011-09-27 17:18:56 +02:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char pidstr[16];
|
|
|
|
|
|
2023-12-19 07:59:41 +01:00
|
|
|
|
snprintf (pidstr, sizeof pidstr, "%10d\n",
|
|
|
|
|
dotlock_get_process_id (h));
|
2011-09-27 17:18:56 +02:00
|
|
|
|
if (write (fd, pidstr, 11 ) == 11
|
|
|
|
|
&& write (fd, h->tname + h->nodename_off,h->nodename_len)
|
|
|
|
|
== h->nodename_len
|
|
|
|
|
&& write (fd, "\n", 1) == 1
|
|
|
|
|
&& !close (fd))
|
|
|
|
|
{
|
|
|
|
|
h->locked = 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/* Write error. */
|
2016-01-13 14:42:12 +01:00
|
|
|
|
saveerrno = errno;
|
2012-06-05 19:29:22 +02:00
|
|
|
|
my_error_2 ("lock not made: writing to '%s' failed: %s\n",
|
2011-09-27 17:18:56 +02:00
|
|
|
|
h->lockname, strerror (errno));
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
|
|
|
|
|
"lock not made: writing to '%s' failed: %s\n",
|
|
|
|
|
h->lockname, strerror (errno));
|
2011-09-27 17:18:56 +02:00
|
|
|
|
close (fd);
|
|
|
|
|
unlink (h->lockname);
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (saveerrno);
|
2011-09-27 17:18:56 +02:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
2011-09-23 14:43:58 +02:00
|
|
|
|
}
|
2011-09-27 17:18:56 +02:00
|
|
|
|
else /* Standard method: Use hardlinks. */
|
2011-09-23 14:43:58 +02:00
|
|
|
|
{
|
2011-09-27 17:18:56 +02:00
|
|
|
|
struct stat sb;
|
|
|
|
|
|
2012-01-03 17:32:41 +01:00
|
|
|
|
/* We ignore the return value of link() because it is unreliable. */
|
|
|
|
|
(void) link (h->tname, h->lockname);
|
2011-09-27 17:18:56 +02:00
|
|
|
|
|
|
|
|
|
if (stat (h->tname, &sb))
|
|
|
|
|
{
|
2016-01-13 14:42:12 +01:00
|
|
|
|
saveerrno = errno;
|
2011-09-27 17:18:56 +02:00
|
|
|
|
my_error_1 ("lock not made: Oops: stat of tmp file failed: %s\n",
|
|
|
|
|
strerror (errno));
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
|
|
|
|
|
"lock not made: Oops: stat of tmp file failed: %s\n",
|
|
|
|
|
strerror (errno));
|
2011-09-27 17:18:56 +02:00
|
|
|
|
/* In theory this might be a severe error: It is possible
|
|
|
|
|
that link succeeded but stat failed due to changed
|
|
|
|
|
permissions. We can't do anything about it, though. */
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (saveerrno);
|
2011-09-27 17:18:56 +02:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sb.st_nlink == 2)
|
|
|
|
|
{
|
|
|
|
|
h->locked = 1;
|
|
|
|
|
return 0; /* Okay. */
|
|
|
|
|
}
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
2011-09-27 17:18:56 +02:00
|
|
|
|
/* Check for stale lock files. */
|
2022-03-18 06:33:53 +01:00
|
|
|
|
if ( (pid = read_lockfile (h, &same_node, &fd)) == -1 )
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2011-09-23 14:43:58 +02:00
|
|
|
|
if ( errno != ENOENT )
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2016-01-13 14:42:12 +01:00
|
|
|
|
saveerrno = errno;
|
2011-09-27 17:18:56 +02:00
|
|
|
|
my_info_0 ("cannot read lockfile\n");
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
|
|
|
|
|
"cannot read lockfile\n");
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (saveerrno);
|
2006-06-28 17:14:09 +02:00
|
|
|
|
return -1;
|
2011-09-23 14:43:58 +02:00
|
|
|
|
}
|
2011-09-27 17:18:56 +02:00
|
|
|
|
my_info_0 ("lockfile disappeared\n");
|
2011-09-28 11:47:40 +02:00
|
|
|
|
goto again;
|
2011-09-23 14:43:58 +02:00
|
|
|
|
}
|
2023-12-19 07:59:41 +01:00
|
|
|
|
else if ( (pid == dotlock_get_process_id (h) && same_node && !h->by_parent)
|
2022-03-19 06:47:59 +01:00
|
|
|
|
|| (same_node && kill (pid, 0) && errno == ESRCH) )
|
2011-09-23 14:43:58 +02:00
|
|
|
|
{
|
2023-12-13 10:08:12 +01:00
|
|
|
|
/* Stale lockfile is detected. */
|
2022-03-18 06:33:53 +01:00
|
|
|
|
struct stat sb;
|
|
|
|
|
|
|
|
|
|
/* Check if it's unlocked during examining the lockfile. */
|
|
|
|
|
if (fstat (fd, &sb) || sb.st_nlink == 0)
|
|
|
|
|
{
|
2022-03-19 06:47:59 +01:00
|
|
|
|
/* It's gone already by another process. */
|
2022-03-18 06:33:53 +01:00
|
|
|
|
close (fd);
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-19 06:47:59 +01:00
|
|
|
|
/*
|
|
|
|
|
* Here, although it's quite _rare_, we have a race condition.
|
|
|
|
|
*
|
|
|
|
|
* When multiple processes race on a stale lockfile, detecting
|
|
|
|
|
* AND removing should be done atomically. That is, to work
|
|
|
|
|
* correctly, the file to be removed should be the one which is
|
|
|
|
|
* examined for detection.
|
|
|
|
|
*
|
|
|
|
|
* But, when it's not atomic, consider the case for us where it
|
|
|
|
|
* takes some time between the detection and the removal of the
|
|
|
|
|
* lockfile.
|
|
|
|
|
*
|
|
|
|
|
* In this situation, it is possible that the file which was
|
|
|
|
|
* detected as stale is already removed by another process and
|
|
|
|
|
* then new lockfile is created (by that process or other one).
|
|
|
|
|
*
|
|
|
|
|
* And it is newly created valid lockfile which is going to be
|
|
|
|
|
* removed by us.
|
|
|
|
|
*
|
|
|
|
|
* Consider this long comment as it expresses possible (long)
|
|
|
|
|
* time between fstat above and unlink below; Meanwhile, the
|
|
|
|
|
* lockfile in question may be removed and there may be new
|
|
|
|
|
* valid one.
|
|
|
|
|
*
|
|
|
|
|
* In short, when you see the message of removing stale lockfile
|
|
|
|
|
* when there are multiple processes for the work, there is
|
|
|
|
|
* (very) little possibility something went wrong.
|
|
|
|
|
*/
|
2022-03-18 06:33:53 +01:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
unlink (h->lockname);
|
2022-03-19 06:47:59 +01:00
|
|
|
|
my_info_1 (_("removing stale lockfile (created by %d)\n"), pid);
|
|
|
|
|
close (fd);
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_STALE_REMOVED,
|
|
|
|
|
_("removing stale lockfile (created by %d)\n"), pid);
|
2011-09-28 11:47:40 +02:00
|
|
|
|
goto again;
|
2011-09-23 14:43:58 +02:00
|
|
|
|
}
|
2006-06-28 17:14:09 +02:00
|
|
|
|
|
2022-03-18 06:33:53 +01:00
|
|
|
|
close (fd);
|
2011-09-28 11:47:40 +02:00
|
|
|
|
if (lastpid == -1)
|
|
|
|
|
lastpid = pid;
|
|
|
|
|
ownerchanged = (pid != lastpid);
|
|
|
|
|
|
|
|
|
|
if (timeout)
|
2011-09-23 14:43:58 +02:00
|
|
|
|
{
|
|
|
|
|
struct timeval tv;
|
2023-10-02 12:53:41 +02:00
|
|
|
|
int wtimereal;
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
2023-10-02 12:53:41 +02:00
|
|
|
|
if (ownerchanged)
|
2024-05-13 00:09:23 +02:00
|
|
|
|
wtime = 0; /* Reset because owner changed. */
|
2011-09-28 11:47:40 +02:00
|
|
|
|
|
2023-10-02 12:53:41 +02:00
|
|
|
|
wtimereal = next_wait_interval (&wtime, &timeout);
|
2023-12-12 16:31:30 +01:00
|
|
|
|
if (!timeout)
|
|
|
|
|
timedout = 1; /* remember. */
|
2023-10-02 12:53:41 +02:00
|
|
|
|
|
|
|
|
|
sumtime += wtimereal;
|
2011-09-28 11:47:40 +02:00
|
|
|
|
if (sumtime >= 1500)
|
|
|
|
|
{
|
|
|
|
|
sumtime = 0;
|
|
|
|
|
my_info_3 (_("waiting for lock (held by %d%s) %s...\n"),
|
|
|
|
|
pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):"");
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb
|
|
|
|
|
&& h->info_cb (h, h->info_cb_value, DOTLOCK_WAITING,
|
|
|
|
|
_("waiting for lock (held by %d%s) %s...\n"),
|
|
|
|
|
pid, maybe_dead,
|
|
|
|
|
maybe_deadlock(h)? _("(deadlock?) "):""))
|
|
|
|
|
{
|
|
|
|
|
my_set_errno (ECANCELED);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2011-09-28 11:47:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 12:53:41 +02:00
|
|
|
|
tv.tv_sec = wtimereal / 1000;
|
|
|
|
|
tv.tv_usec = (wtimereal % 1000) * 1000;
|
2011-09-23 14:43:58 +02:00
|
|
|
|
select (0, NULL, NULL, NULL, &tv);
|
2011-09-28 11:47:40 +02:00
|
|
|
|
goto again;
|
2011-09-23 14:43:58 +02:00
|
|
|
|
}
|
2006-06-28 17:14:09 +02:00
|
|
|
|
|
2023-12-12 16:31:30 +01:00
|
|
|
|
my_set_errno (timedout? ETIMEDOUT : EACCES);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_POSIX_SYSTEM*/
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2006-06-28 17:14:09 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
2011-09-28 11:47:40 +02:00
|
|
|
|
/* Windows specific code of make_dotlock. Returns 0 on success and -1 on
|
|
|
|
|
error. */
|
2011-09-23 14:43:58 +02:00
|
|
|
|
static int
|
2011-09-28 11:47:40 +02:00
|
|
|
|
dotlock_take_w32 (dotlock_t h, long timeout)
|
2011-09-23 14:43:58 +02:00
|
|
|
|
{
|
2011-09-28 11:47:40 +02:00
|
|
|
|
int wtime = 0;
|
2023-12-12 16:31:30 +01:00
|
|
|
|
int timedout = 0;
|
2011-09-23 14:43:58 +02:00
|
|
|
|
int w32err;
|
|
|
|
|
OVERLAPPED ovl;
|
2006-06-28 17:14:09 +02:00
|
|
|
|
|
2011-09-28 11:47:40 +02:00
|
|
|
|
again:
|
2011-09-23 14:43:58 +02:00
|
|
|
|
/* Lock one byte at offset 0. The offset is given by OVL. */
|
|
|
|
|
memset (&ovl, 0, sizeof ovl);
|
|
|
|
|
if (LockFileEx (h->lockhd, (LOCKFILE_EXCLUSIVE_LOCK
|
|
|
|
|
| LOCKFILE_FAIL_IMMEDIATELY), 0, 1, 0, &ovl))
|
|
|
|
|
{
|
|
|
|
|
h->locked = 1;
|
|
|
|
|
return 0; /* okay */
|
|
|
|
|
}
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
w32err = GetLastError ();
|
|
|
|
|
if (w32err != ERROR_LOCK_VIOLATION)
|
|
|
|
|
{
|
2012-06-05 19:29:22 +02:00
|
|
|
|
my_error_2 (_("lock '%s' not made: %s\n"),
|
2011-09-27 17:18:56 +02:00
|
|
|
|
h->lockname, w32_strerror (w32err));
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
|
|
|
|
|
_("lock '%s' not made: %s\n"),
|
|
|
|
|
h->lockname, w32_strerror (w32err));
|
|
|
|
|
_set_errno (map_w32_to_errno (w32err));
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2011-09-28 11:47:40 +02:00
|
|
|
|
if (timeout)
|
2011-09-23 14:43:58 +02:00
|
|
|
|
{
|
2023-10-02 12:53:41 +02:00
|
|
|
|
int wtimereal;
|
|
|
|
|
|
|
|
|
|
wtimereal = next_wait_interval (&wtime, &timeout);
|
2023-12-12 16:31:30 +01:00
|
|
|
|
if (!timeout)
|
|
|
|
|
timedout = 1; /* remember. */
|
2011-09-28 11:47:40 +02:00
|
|
|
|
|
|
|
|
|
if (wtime >= 800)
|
2023-12-13 10:08:12 +01:00
|
|
|
|
{
|
|
|
|
|
my_info_1 (_("waiting for lock %s...\n"), h->lockname);
|
|
|
|
|
if (h->info_cb
|
|
|
|
|
&& h->info_cb (h, h->info_cb_value, DOTLOCK_WAITING,
|
|
|
|
|
_("waiting for lock %s...\n"), h->lockname))
|
|
|
|
|
{
|
|
|
|
|
my_set_errno (ECANCELED);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-28 11:47:40 +02:00
|
|
|
|
|
2023-10-02 12:53:41 +02:00
|
|
|
|
Sleep (wtimereal);
|
2011-09-28 11:47:40 +02:00
|
|
|
|
goto again;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
2023-12-12 16:31:30 +01:00
|
|
|
|
my_set_errno (timedout? ETIMEDOUT : EACCES);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return -1;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
2011-09-23 14:43:58 +02:00
|
|
|
|
#endif /*HAVE_DOSISH_SYSTEM*/
|
2003-01-09 13:36:05 +01:00
|
|
|
|
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
/* Take a lock on H. A value of 0 for TIMEOUT returns immediately if
|
2019-12-23 12:36:24 +01:00
|
|
|
|
the lock can't be taken, -1 waits forever (hopefully not), other
|
2011-09-28 11:47:40 +02:00
|
|
|
|
values wait for TIMEOUT milliseconds. Returns: 0 on success */
|
2003-01-09 13:36:05 +01:00
|
|
|
|
int
|
2011-09-23 14:43:58 +02:00
|
|
|
|
dotlock_take (dotlock_t h, long timeout)
|
2003-01-09 13:36:05 +01:00
|
|
|
|
{
|
2011-09-23 14:43:58 +02:00
|
|
|
|
int ret;
|
2006-05-23 18:19:43 +02:00
|
|
|
|
|
2006-06-28 17:14:09 +02:00
|
|
|
|
if ( h->disable )
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return 0; /* Locks are completely disabled. Return success. */
|
2003-06-18 21:56:13 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
if ( h->locked )
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2012-06-05 19:29:22 +02:00
|
|
|
|
my_debug_1 ("Oops, '%s' is already locked\n", h->lockname);
|
2006-06-28 17:14:09 +02:00
|
|
|
|
return 0;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
2008-06-13 18:18:59 +02:00
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
2011-09-28 11:47:40 +02:00
|
|
|
|
ret = dotlock_take_w32 (h, timeout);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
#else /*!HAVE_DOSISH_SYSTEM*/
|
2011-09-28 11:47:40 +02:00
|
|
|
|
ret = dotlock_take_unix (h, timeout);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
#endif /*!HAVE_DOSISH_SYSTEM*/
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_POSIX_SYSTEM
|
|
|
|
|
/* Unix specific code of release_dotlock. */
|
|
|
|
|
static int
|
|
|
|
|
dotlock_release_unix (dotlock_t h)
|
|
|
|
|
{
|
|
|
|
|
int pid, same_node;
|
2016-01-13 14:42:12 +01:00
|
|
|
|
int saveerrno;
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2022-03-18 06:33:53 +01:00
|
|
|
|
pid = read_lockfile (h, &same_node, NULL);
|
2011-02-04 12:57:53 +01:00
|
|
|
|
if ( pid == -1 )
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2016-01-13 14:42:12 +01:00
|
|
|
|
saveerrno = errno;
|
2011-09-27 17:18:56 +02:00
|
|
|
|
my_error_0 ("release_dotlock: lockfile error\n");
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
|
|
|
|
|
"release_dotlock: lockfile error\n");
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (saveerrno);
|
2006-06-28 17:14:09 +02:00
|
|
|
|
return -1;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
2023-12-19 07:59:41 +01:00
|
|
|
|
if ( pid != dotlock_get_process_id (h) || !same_node )
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2011-09-27 17:18:56 +02:00
|
|
|
|
my_error_1 ("release_dotlock: not our lock (pid=%d)\n", pid);
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_CONFLICT,
|
|
|
|
|
"release_dotlock: not our lock (pid=%d)\n", pid);
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (EACCES);
|
2006-06-28 17:14:09 +02:00
|
|
|
|
return -1;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
2008-06-13 18:18:59 +02:00
|
|
|
|
|
2006-06-28 17:14:09 +02:00
|
|
|
|
if ( unlink( h->lockname ) )
|
|
|
|
|
{
|
2016-01-13 14:42:12 +01:00
|
|
|
|
saveerrno = errno;
|
2012-06-05 19:29:22 +02:00
|
|
|
|
my_error_1 ("release_dotlock: error removing lockfile '%s'\n",
|
2011-09-27 17:18:56 +02:00
|
|
|
|
h->lockname);
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
|
|
|
|
|
"release_dotlock: error removing lockfile '%s'\n",
|
|
|
|
|
h->lockname);
|
2016-01-13 14:42:12 +01:00
|
|
|
|
my_set_errno (saveerrno);
|
2006-06-28 17:14:09 +02:00
|
|
|
|
return -1;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
2008-06-13 18:18:59 +02:00
|
|
|
|
/* Fixme: As an extra check we could check whether the link count is
|
|
|
|
|
now really at 1. */
|
2006-06-28 17:14:09 +02:00
|
|
|
|
return 0;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
2011-09-23 14:43:58 +02:00
|
|
|
|
#endif /*HAVE_POSIX_SYSTEM */
|
2003-01-09 13:36:05 +01:00
|
|
|
|
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
|
|
|
|
/* Windows specific code of release_dotlock. */
|
2003-01-09 13:36:05 +01:00
|
|
|
|
static int
|
2011-09-23 14:43:58 +02:00
|
|
|
|
dotlock_release_w32 (dotlock_t h)
|
2003-01-09 13:36:05 +01:00
|
|
|
|
{
|
2011-09-23 14:43:58 +02:00
|
|
|
|
OVERLAPPED ovl;
|
2006-06-28 17:14:09 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
memset (&ovl, 0, sizeof ovl);
|
|
|
|
|
if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl))
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2023-12-13 10:08:12 +01:00
|
|
|
|
int ec = (int)GetLastError ();
|
|
|
|
|
|
2012-06-05 19:29:22 +02:00
|
|
|
|
my_error_2 ("release_dotlock: error removing lockfile '%s': %s\n",
|
2023-12-13 10:08:12 +01:00
|
|
|
|
h->lockname, w32_strerror (ec));
|
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
|
|
|
|
|
"release_dotlock: error removing lockfile '%s': %s\n",
|
|
|
|
|
h->lockname, w32_strerror (ec));
|
|
|
|
|
my_set_errno (map_w32_to_errno (ec));
|
2006-06-28 17:14:09 +02:00
|
|
|
|
return -1;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
2006-06-28 17:14:09 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_DOSISH_SYSTEM */
|
2006-06-28 17:14:09 +02:00
|
|
|
|
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
/* Release a lock. Returns 0 on success. */
|
|
|
|
|
int
|
|
|
|
|
dotlock_release (dotlock_t h)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* To avoid atexit race conditions we first check whether there are
|
|
|
|
|
any locks left. It might happen that another atexit handler
|
|
|
|
|
tries to release the lock while the atexit handler of this module
|
|
|
|
|
already ran and thus H is undefined. */
|
2011-09-29 15:27:01 +02:00
|
|
|
|
LOCK_all_lockfiles ();
|
|
|
|
|
ret = !all_lockfiles;
|
|
|
|
|
UNLOCK_all_lockfiles ();
|
|
|
|
|
if (ret)
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if ( h->disable )
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if ( !h->locked )
|
2006-06-28 17:14:09 +02:00
|
|
|
|
{
|
2012-06-05 19:29:22 +02:00
|
|
|
|
my_debug_1 ("Oops, '%s' is not locked\n", h->lockname);
|
2023-12-13 10:08:12 +01:00
|
|
|
|
if (h->info_cb)
|
|
|
|
|
h->info_cb (h, h->info_cb_value, DOTLOCK_NOT_LOCKED,
|
|
|
|
|
"Oops, '%s' is not locked\n", h->lockname);
|
2011-09-23 14:43:58 +02:00
|
|
|
|
return 0;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
2006-06-28 17:14:09 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
|
|
|
|
ret = dotlock_release_w32 (h);
|
|
|
|
|
#else
|
|
|
|
|
ret = dotlock_release_unix (h);
|
|
|
|
|
#endif
|
2006-06-28 17:14:09 +02:00
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
if (!ret)
|
|
|
|
|
h->locked = 0;
|
|
|
|
|
return ret;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
|
2011-09-29 15:27:01 +02:00
|
|
|
|
/* Remove all lockfiles. This is called by the atexit handler
|
2008-06-13 18:18:59 +02:00
|
|
|
|
installed by this module but may also be called by other
|
|
|
|
|
termination handlers. */
|
2003-06-18 21:56:13 +02:00
|
|
|
|
void
|
2009-09-23 12:28:41 +02:00
|
|
|
|
dotlock_remove_lockfiles (void)
|
2003-01-09 13:36:05 +01:00
|
|
|
|
{
|
2009-09-23 12:28:41 +02:00
|
|
|
|
dotlock_t h, h2;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2011-09-29 15:27:01 +02:00
|
|
|
|
/* First set the lockfiles list to NULL so that for example
|
2015-11-16 12:41:46 +01:00
|
|
|
|
dotlock_release is aware that this function is currently
|
2011-09-29 15:27:01 +02:00
|
|
|
|
running. */
|
|
|
|
|
LOCK_all_lockfiles ();
|
2006-05-23 18:19:43 +02:00
|
|
|
|
h = all_lockfiles;
|
|
|
|
|
all_lockfiles = NULL;
|
2011-09-29 15:27:01 +02:00
|
|
|
|
UNLOCK_all_lockfiles ();
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2006-05-23 18:19:43 +02:00
|
|
|
|
while ( h )
|
|
|
|
|
{
|
|
|
|
|
h2 = h->next;
|
2011-09-23 14:43:58 +02:00
|
|
|
|
dotlock_destroy (h);
|
2006-05-23 18:19:43 +02:00
|
|
|
|
h = h2;
|
2003-01-09 13:36:05 +01:00
|
|
|
|
}
|
|
|
|
|
}
|