summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--DotLock.hs44
-rw-r--r--dotlock.c1303
-rw-r--r--dotlock.h112
-rw-r--r--kiki.cabal1
4 files changed, 1460 insertions, 0 deletions
diff --git a/DotLock.hs b/DotLock.hs
new file mode 100644
index 0000000..257a033
--- /dev/null
+++ b/DotLock.hs
@@ -0,0 +1,44 @@
1module DotLock
2 ( DotLock
3 , Flags
4 , dotlock_init
5 , dotlock_create
6 , dotlock_take
7 , dotlock_release
8 , dotlock_destroy
9 , dotlock_remove_lockfiles
10 , dotlock_set_fd
11 , dotlock_get_fd
12 , dotlock_disable
13 ) where
14
15import System.Posix.Types (Fd(..))
16import Foreign.C.String
17import Foreign.C.Types
18import Foreign.Ptr
19
20newtype DotLock = DotLockPtr (Ptr ())
21type Flags = Int
22
23foreign import ccall "dotlock_create" _dotlock_create_ptr :: Ptr Char -> Flags -> IO (Ptr ())
24
25foreign import ccall "dotlock_create" _dotlock_create :: CString -> Flags -> IO (Ptr ())
26
27dotlock_init :: IO ()
28dotlock_init = do
29 null_ptr <- _dotlock_create_ptr nullPtr 0
30 return ()
31
32dotlock_create :: FilePath -> Flags -> IO (Maybe DotLock)
33dotlock_create file flags = do
34 ptr <- withCString file (flip _dotlock_create flags)
35 if ptr == nullPtr then return Nothing else return (Just $ DotLockPtr ptr)
36
37
38foreign import ccall "dotlock_take" dotlock_take :: DotLock -> CLong -> IO CInt
39foreign import ccall "dotlock_release" dotlock_release :: DotLock -> IO CInt
40foreign import ccall "dotlock_destroy" dotlock_destroy :: DotLock -> IO ()
41foreign import ccall "dotlock_remove_lockfiles" dotlock_remove_lockfiles ::DotLock -> IO ()
42foreign import ccall "dotlock_set_fd" dotlock_set_fd :: DotLock -> Fd -> IO ()
43foreign import ccall "dotlock_get_fd" dotlock_get_fd :: DotLock -> IO Fd
44foreign import ccall "dotlock_disable" dotlock_disable :: IO ()
diff --git a/dotlock.c b/dotlock.c
new file mode 100644
index 0000000..c111159
--- /dev/null
+++ b/dotlock.c
@@ -0,0 +1,1303 @@
1/* dotlock.c - dotfile locking
2 * Copyright (C) 1998, 2000, 2001, 2003, 2004,
3 * 2005, 2006, 2008, 2010, 2011 Free Software Foundation, Inc.
4 *
5 * This file is part of JNLIB, which is a subsystem of GnuPG.
6 *
7 * JNLIB is free software; you can redistribute it and/or modify it
8 * under the terms of either
9 *
10 * - the GNU Lesser General Public License as published by the Free
11 * Software Foundation; either version 3 of the License, or (at
12 * your option) any later version.
13 *
14 * or
15 *
16 * - the GNU General Public License as published by the Free
17 * Software Foundation; either version 2 of the License, or (at
18 * your option) any later version.
19 *
20 * or both in parallel, as here.
21 *
22 * JNLIB is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
26 *
27 * You should have received a copies of the GNU General Public License
28 * and the GNU Lesser General Public License along with this program;
29 * if not, see <http://www.gnu.org/licenses/>.
30 *
31 * ALTERNATIVELY, this file may be distributed under the terms of the
32 * following license, in which case the provisions of this license are
33 * required INSTEAD OF the GNU Lesser General License or the GNU
34 * General Public License. If you wish to allow use of your version of
35 * this file only under the terms of the GNU Lesser General License or
36 * the GNU General Public License, and not to allow others to use your
37 * version of this file under the terms of the following license,
38 * indicate your decision by deleting this paragraph and the license
39 * below.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 *
45 * 1. Redistributions of source code must retain the above copyright
46 * notice, and the entire permission notice in its entirety,
47 * including the disclaimer of warranties.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. The name of the author may not be used to endorse or promote
52 * products derived from this software without specific prior
53 * written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
56 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
57 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
58 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
59 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
60 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
61 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
63 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
64 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
65 * OF THE POSSIBILITY OF SUCH DAMAGE.
66 */
67
68/*
69 Overview:
70 =========
71
72 This module implements advisory file locking in a portable way.
73 Due to the problems with POSIX fcntl locking a separate lock file
74 is used. It would be possible to use fcntl locking on this lock
75 file and thus avoid the weird auto unlock bug of POSIX while still
76 having an unproved better performance of fcntl locking. However
77 there are still problems left, thus we resort to use a hardlink
78 which has the well defined property that a link call will fail if
79 the target file already exists.
80
81 Given that hardlinks are also available on NTFS file systems since
82 Windows XP; it will be possible to enhance this module to use
83 hardlinks even on Windows and thus allow Windows and Posix clients
84 to use locking on the same directory. This is not yet implemented;
85 instead we use a lockfile on Windows along with W32 style file
86 locking.
87
88 On FAT file systems hardlinks are not supported. Thus this method
89 does not work. Our solution is to use a O_EXCL locking instead.
90 Querying the type of the file system is not easy to do in a
91 portable way (e.g. Linux has a statfs, BSDs have a the same call
92 but using different structures and constants). What we do instead
93 is to check at runtime whether link(2) works for a specific lock
94 file.
95
96
97 How to use:
98 ===========
99
100 At program initialization time, the module should be explicitly
101 initialized:
102
103 dotlock_create (NULL, 0);
104
105 This installs an atexit handler and may also initialize mutex etc.
106 It is optional for non-threaded applications. Only the first call
107 has an effect. This needs to be done before any extra threads are
108 started.
109
110 To create a lock file (which prepares it but does not take the
111 lock) you do:
112
113 dotlock_t h
114
115 h = dotlock_create (fname, 0);
116 if (!h)
117 error ("error creating lock file: %s\n", strerror (errno));
118
119 It is important to handle the error. For example on a read-only
120 file system a lock can't be created (but is usually not needed).
121 FNAME is the file you want to lock; the actual lockfile is that
122 name with the suffix ".lock" appended. On success a handle to be
123 used with the other functions is returned or NULL on error. Note
124 that the handle shall only be used by one thread at a time. This
125 function creates a unique file temporary file (".#lk*") in the same
126 directory as FNAME and returns a handle for further operations.
127 The module keeps track of theses unique files so that they will be
128 unlinked using the atexit handler. If you don't need the lock file
129 anymore, you may also explicitly remove it with a call to:
130
131 dotlock_destroy (h);
132
133 To actually lock the file, you use:
134
135 if (dotlock_take (h, -1))
136 error ("error taking lock: %s\n", strerror (errno));
137
138 This function will wait until the lock is acquired. If an
139 unexpected error occurs if will return non-zero and set ERRNO. If
140 you pass (0) instead of (-1) the function does not wait in case the
141 file is already locked but returns -1 and sets ERRNO to EACCES.
142 Any other positive value for the second parameter is considered a
143 timeout valuie in milliseconds.
144
145 To release the lock you call:
146
147 if (dotlock_release (h))
148 error ("error releasing lock: %s\n", strerror (errno));
149
150 or, if the lock file is not anymore needed, you may just call
151 dotlock_destroy. However dotlock_release does some extra checks
152 before releasing the lock and prints diagnostics to help detecting
153 bugs.
154
155 If you want to explicitly destroy all lock files you may call
156
157 dotlock_remove_lockfiles ();
158
159 which is the core of the installed atexit handler. In case your
160 application wants to disable locking completely it may call
161
162 disable_locking ()
163
164 before any locks are created.
165
166 There are two convenience functions to store an integer (e.g. a
167 file descriptor) value with the handle:
168
169 void dotlock_set_fd (dotlock_t h, int fd);
170 int dotlock_get_fd (dotlock_t h);
171
172 If nothing has been stored dotlock_get_fd returns -1.
173
174
175
176 How to build:
177 =============
178
179 This module was originally developed for GnuPG but later changed to
180 allow its use without any GnuPG dependency. If you want to use it
181 with you application you may simply use it and it should figure out
182 most things automagically.
183
184 You may use the common config.h file to pass macros, but take care
185 to pass -DHAVE_CONFIG_H to the compiler. Macros used by this
186 module are:
187
188 DOTLOCK_USE_PTHREAD - Define if POSIX threads are in use.
189
190 DOTLOCK_GLIB_LOGGING - Define this to use Glib logging functions.
191
192 DOTLOCK_EXT_SYM_PREFIX - Prefix all external symbols with the
193 string to which this macro evaluates.
194
195 GNUPG_MAJOR_VERSION - Defined when used by GnuPG.
196
197 HAVE_DOSISH_SYSTEM - Defined for Windows etc. Will be
198 automatically defined if a the target is
199 Windows.
200
201 HAVE_POSIX_SYSTEM - Internally defined to !HAVE_DOSISH_SYSTEM.
202
203 HAVE_SIGNAL_H - Should be defined on Posix systems. If config.h
204 is not used defaults to defined.
205
206 DIRSEP_C - Separation character for file name parts.
207 Usually not redefined.
208
209 EXTSEP_S - Separation string for file name suffixes.
210 Usually not redefined.
211
212 HAVE_W32CE_SYSTEM - Currently only used by GnuPG.
213
214 Note that there is a test program t-dotlock which has compile
215 instructions at its end. At least for SMBFS and CIFS it is
216 important that 64 bit versions of stat are used; most programming
217 environments do this these days, just in case you want to compile
218 it on the command line, remember to pass -D_FILE_OFFSET_BITS=64
219
220
221 Bugs:
222 =====
223
224 On Windows this module is not yet thread-safe.
225
226
227 Miscellaneous notes:
228 ====================
229
230 On hardlinks:
231 - Hardlinks are supported under Windows with NTFS since XP/Server2003.
232 - In Linux 2.6.33 both SMBFS and CIFS seem to support hardlinks.
233 - NFS supports hard links. But there are solvable problems.
234 - FAT does not support links
235
236 On the file locking API:
237 - CIFS on Linux 2.6.33 supports several locking methods.
238 SMBFS seems not to support locking. No closer checks done.
239 - NFS supports Posix locks. flock is emulated in the server.
240 However there are a couple of problems; see below.
241 - FAT does not support locks.
242 - An advantage of fcntl locking is that R/W locks can be
243 implemented which is not easy with a straight lock file.
244
245 On O_EXCL:
246 - Does not work reliable on NFS
247 - Should work on CIFS and SMBFS but how can we delete lockfiles?
248
249 On NFS problems:
250 - Locks vanish if the server crashes and reboots.
251 - Client crashes keep the lock in the server until the client
252 re-connects.
253 - Communication problems may return unreliable error codes. The
254 MUA Postfix's workaround is to compare the link count after
255 seeing an error for link. However that gives a race. If using a
256 unique file to link to a lockfile and using stat to check the
257 link count instead of looking at the error return of link(2) is
258 the best solution.
259 - O_EXCL seems to have a race and may re-create a file anyway.
260
261*/
262
263#ifdef HAVE_CONFIG_H
264# include <config.h>
265#endif
266
267/* Some quick replacements for stuff we usually expect to be defined
268 in config.h. Define HAVE_POSIX_SYSTEM for better readability. */
269#if !defined (HAVE_DOSISH_SYSTEM) && defined(_WIN32)
270# define HAVE_DOSISH_SYSTEM 1
271#endif
272#if !defined (HAVE_DOSISH_SYSTEM) && !defined (HAVE_POSIX_SYSTEM)
273# define HAVE_POSIX_SYSTEM 1
274#endif
275
276/* With no config.h assume that we have sitgnal.h. */
277#if !defined (HAVE_CONFIG_H) && defined (HAVE_POSIX_SYSTEM)
278# define HAVE_SIGNAL_H 1
279#endif
280
281/* Standard headers. */
282#include <stdlib.h>
283#include <stdio.h>
284#include <string.h>
285#include <errno.h>
286#include <ctype.h>
287#include <errno.h>
288#include <unistd.h>
289#ifdef HAVE_DOSISH_SYSTEM
290# define WIN32_LEAN_AND_MEAN /* We only need the OS core stuff. */
291# include <windows.h>
292#else
293# include <sys/types.h>
294# include <sys/stat.h>
295# include <sys/utsname.h>
296#endif
297#include <sys/types.h>
298#include <sys/time.h>
299#include <sys/stat.h>
300#include <fcntl.h>
301#ifdef HAVE_SIGNAL_H
302# include <signal.h>
303#endif
304#ifdef DOTLOCK_USE_PTHREAD
305# include <pthread.h>
306#endif
307
308#ifdef DOTLOCK_GLIB_LOGGING
309# include <glib.h>
310#endif
311
312#ifdef GNUPG_MAJOR_VERSION
313# include "libjnlib-config.h"
314#endif
315#ifdef HAVE_W32CE_SYSTEM
316# include "utf8conv.h" /* WindowsCE requires filename conversion. */
317#endif
318
319#include "dotlock.h"
320
321
322/* Define constants for file name construction. */
323#if !defined(DIRSEP_C) && !defined(EXTSEP_S)
324# ifdef HAVE_DOSISH_SYSTEM
325# define DIRSEP_C '\\'
326# define EXTSEP_S "."
327#else
328# define DIRSEP_C '/'
329# define EXTSEP_S "."
330# endif
331#endif
332
333/* In GnuPG we use wrappers around the malloc fucntions. If they are
334 not defined we assume that this code is used outside of GnuPG and
335 fall back to the regular malloc functions. */
336#ifndef jnlib_malloc
337# define jnlib_malloc(a) malloc ((a))
338# define jnlib_calloc(a,b) calloc ((a), (b))
339# define jnlib_free(a) free ((a))
340#endif
341
342/* Wrapper to set ERRNO. */
343#ifndef jnlib_set_errno
344# ifdef HAVE_W32CE_SYSTEM
345# define jnlib_set_errno(e) gpg_err_set_errno ((e))
346# else
347# define jnlib_set_errno(e) do { errno = (e); } while (0)
348# endif
349#endif
350
351/* Gettext macro replacement. */
352#ifndef _
353# define _(a) (a)
354#endif
355
356#ifdef GNUPG_MAJOR_VERSION
357# define my_info_0(a) log_info ((a))
358# define my_info_1(a,b) log_info ((a), (b))
359# define my_info_2(a,b,c) log_info ((a), (b), (c))
360# define my_info_3(a,b,c,d) log_info ((a), (b), (c), (d))
361# define my_error_0(a) log_error ((a))
362# define my_error_1(a,b) log_error ((a), (b))
363# define my_error_2(a,b,c) log_error ((a), (b), (c))
364# define my_debug_1(a,b) log_debug ((a), (b))
365# define my_fatal_0(a) log_fatal ((a))
366#elif defined (DOTLOCK_GLIB_LOGGING)
367# define my_info_0(a) g_message ((a))
368# define my_info_1(a,b) g_message ((a), (b))
369# define my_info_2(a,b,c) g_message ((a), (b), (c))
370# define my_info_3(a,b,c,d) g_message ((a), (b), (c), (d))
371# define my_error_0(a) g_warning ((a))
372# define my_error_1(a,b) g_warning ((a), (b))
373# define my_error_2(a,b,c) g_warning ((a), (b), (c))
374# define my_debug_1(a,b) g_debug ((a), (b))
375# define my_fatal_0(a) g_error ((a))
376#else
377# define my_info_0(a) fprintf (stderr, (a))
378# define my_info_1(a,b) fprintf (stderr, (a), (b))
379# define my_info_2(a,b,c) fprintf (stderr, (a), (b), (c))
380# define my_info_3(a,b,c,d) fprintf (stderr, (a), (b), (c), (d))
381# define my_error_0(a) fprintf (stderr, (a))
382# define my_error_1(a,b) fprintf (stderr, (a), (b))
383# define my_error_2(a,b,c) fprintf (stderr, (a), (b), (c))
384# define my_debug_1(a,b) fprintf (stderr, (a), (b))
385# define my_fatal_0(a) do { fprintf (stderr,(a)); fflush (stderr); \
386 abort (); } while (0)
387#endif
388
389
390
391
392
393/* The object describing a lock. */
394struct dotlock_handle
395{
396 struct dotlock_handle *next;
397 char *lockname; /* Name of the actual lockfile. */
398 unsigned int locked:1; /* Lock status. */
399 unsigned int disable:1; /* If true, locking is disabled. */
400 unsigned int use_o_excl:1; /* Use open (O_EXCL) for locking. */
401
402 int extra_fd; /* A place for the caller to store an FD. */
403
404#ifdef HAVE_DOSISH_SYSTEM
405 HANDLE lockhd; /* The W32 handle of the lock file. */
406#else /*!HAVE_DOSISH_SYSTEM */
407 char *tname; /* Name of the lockfile template. */
408 size_t nodename_off; /* Offset in TNAME of the nodename part. */
409 size_t nodename_len; /* Length of the nodename part. */
410#endif /*!HAVE_DOSISH_SYSTEM */
411};
412
413
414/* A list of of all lock handles. The volatile attribute might help
415 if used in an atexit handler. */
416static volatile dotlock_t all_lockfiles;
417#ifdef DOTLOCK_USE_PTHREAD
418static pthread_mutex_t all_lockfiles_mutex = PTHREAD_MUTEX_INITIALIZER;
419# define LOCK_all_lockfiles() do { \
420 if (pthread_mutex_lock (&all_lockfiles_mutex)) \
421 my_fatal_0 ("locking all_lockfiles_mutex failed\n"); \
422 } while (0)
423# define UNLOCK_all_lockfiles() do { \
424 if (pthread_mutex_unlock (&all_lockfiles_mutex)) \
425 my_fatal_0 ("unlocking all_lockfiles_mutex failed\n"); \
426 } while (0)
427#else /*!DOTLOCK_USE_PTHREAD*/
428# define LOCK_all_lockfiles() do { } while (0)
429# define UNLOCK_all_lockfiles() do { } while (0)
430#endif /*!DOTLOCK_USE_PTHREAD*/
431
432/* If this has the value true all locking is disabled. */
433static int never_lock;
434
435
436
437
438
439/* Entirely disable all locking. This function should be called
440 before any locking is done. It may be called right at startup of
441 the process as it only sets a global value. */
442void
443dotlock_disable (void)
444{
445 never_lock = 1;
446}
447
448
449#ifdef HAVE_POSIX_SYSTEM
450static int
451maybe_deadlock (dotlock_t h)
452{
453 dotlock_t r;
454 int res = 0;
455
456 LOCK_all_lockfiles ();
457 for (r=all_lockfiles; r; r = r->next)
458 {
459 if ( r != h && r->locked )
460 {
461 res = 1;
462 break;
463 }
464 }
465 UNLOCK_all_lockfiles ();
466 return res;
467}
468#endif /*HAVE_POSIX_SYSTEM*/
469
470
471/* Read the lock file and return the pid, returns -1 on error. True
472 will be stored in the integer at address SAME_NODE if the lock file
473 has been created on the same node. */
474#ifdef HAVE_POSIX_SYSTEM
475static int
476read_lockfile (dotlock_t h, int *same_node )
477{
478 char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node
479 names are usually shorter. */
480 int fd;
481 int pid = -1;
482 char *buffer, *p;
483 size_t expected_len;
484 int res, nread;
485
486 *same_node = 0;
487 expected_len = 10 + 1 + h->nodename_len + 1;
488 if ( expected_len >= sizeof buffer_space)
489 {
490 buffer = jnlib_malloc (expected_len);
491 if (!buffer)
492 return -1;
493 }
494 else
495 buffer = buffer_space;
496
497 if ( (fd = open (h->lockname, O_RDONLY)) == -1 )
498 {
499 int e = errno;
500 my_info_2 ("error opening lockfile '%s': %s\n",
501 h->lockname, strerror(errno) );
502 if (buffer != buffer_space)
503 jnlib_free (buffer);
504 jnlib_set_errno (e); /* Need to return ERRNO here. */
505 return -1;
506 }
507
508 p = buffer;
509 nread = 0;
510 do
511 {
512 res = read (fd, p, expected_len - nread);
513 if (res == -1 && errno == EINTR)
514 continue;
515 if (res < 0)
516 {
517 my_info_1 ("error reading lockfile '%s'\n", h->lockname );
518 close (fd);
519 if (buffer != buffer_space)
520 jnlib_free (buffer);
521 jnlib_set_errno (0); /* Do not return an inappropriate ERRNO. */
522 return -1;
523 }
524 p += res;
525 nread += res;
526 }
527 while (res && nread != expected_len);
528 close(fd);
529
530 if (nread < 11)
531 {
532 my_info_1 ("invalid size of lockfile '%s'\n", h->lockname);
533 if (buffer != buffer_space)
534 jnlib_free (buffer);
535 jnlib_set_errno (0); /* Better don't return an inappropriate ERRNO. */
536 return -1;
537 }
538
539 if (buffer[10] != '\n'
540 || (buffer[10] = 0, pid = atoi (buffer)) == -1
541 || !pid )
542 {
543 my_error_2 ("invalid pid %d in lockfile '%s'\n", pid, h->lockname);
544 if (buffer != buffer_space)
545 jnlib_free (buffer);
546 jnlib_set_errno (0);
547 return -1;
548 }
549
550 if (nread == expected_len
551 && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len)
552 && buffer[11+h->nodename_len] == '\n')
553 *same_node = 1;
554
555 if (buffer != buffer_space)
556 jnlib_free (buffer);
557 return pid;
558}
559#endif /*HAVE_POSIX_SYSTEM */
560
561
562/* Check whether the file system which stores TNAME supports
563 hardlinks. Instead of using the non-portable statsfs call which
564 differs between various Unix versions, we do a runtime test.
565 Returns: 0 supports hardlinks; 1 no hardlink support, -1 unknown
566 (test error). */
567#ifdef HAVE_POSIX_SYSTEM
568static int
569use_hardlinks_p (const char *tname)
570{
571 char *lname;
572 struct stat sb;
573 unsigned int nlink;
574 int res;
575
576 if (stat (tname, &sb))
577 return -1;
578 nlink = (unsigned int)sb.st_nlink;
579
580 lname = jnlib_malloc (strlen (tname) + 1 + 1);
581 if (!lname)
582 return -1;
583 strcpy (lname, tname);
584 strcat (lname, "x");
585
586 /* We ignore the return value of link() because it is unreliable. */
587 (void) link (tname, lname);
588
589 if (stat (tname, &sb))
590 res = -1; /* Ooops. */
591 else if (sb.st_nlink == nlink + 1)
592 res = 0; /* Yeah, hardlinks are supported. */
593 else
594 res = 1; /* No hardlink support. */
595
596 unlink (lname);
597 jnlib_free (lname);
598 return res;
599}
600#endif /*HAVE_POSIX_SYSTEM */
601
602
603
604#ifdef HAVE_POSIX_SYSTEM
605/* Locking core for Unix. It used a temporary file and the link
606 system call to make locking an atomic operation. */
607static dotlock_t
608dotlock_create_unix (dotlock_t h, const char *file_to_lock)
609{
610 int fd = -1;
611 char pidstr[16];
612 const char *nodename;
613 const char *dirpart;
614 int dirpartlen;
615 struct utsname utsbuf;
616 size_t tnamelen;
617
618 snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() );
619
620 /* Create a temporary file. */
621 if ( uname ( &utsbuf ) )
622 nodename = "unknown";
623 else
624 nodename = utsbuf.nodename;
625
626 if ( !(dirpart = strrchr (file_to_lock, DIRSEP_C)) )
627 {
628 dirpart = EXTSEP_S;
629 dirpartlen = 1;
630 }
631 else
632 {
633 dirpartlen = dirpart - file_to_lock;
634 dirpart = file_to_lock;
635 }
636
637 LOCK_all_lockfiles ();
638 h->next = all_lockfiles;
639 all_lockfiles = h;
640
641 tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10 + 1;
642 h->tname = jnlib_malloc (tnamelen + 1);
643 if (!h->tname)
644 {
645 all_lockfiles = h->next;
646 UNLOCK_all_lockfiles ();
647 jnlib_free (h);
648 return NULL;
649 }
650 h->nodename_len = strlen (nodename);
651
652 snprintf (h->tname, tnamelen, "%.*s/.#lk%p.", dirpartlen, dirpart, h );
653 h->nodename_off = strlen (h->tname);
654 snprintf (h->tname+h->nodename_off, tnamelen - h->nodename_off,
655 "%s.%d", nodename, (int)getpid ());
656
657 do
658 {
659 jnlib_set_errno (0);
660 fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL,
661 S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
662 }
663 while (fd == -1 && errno == EINTR);
664
665 if ( fd == -1 )
666 {
667 all_lockfiles = h->next;
668 UNLOCK_all_lockfiles ();
669 my_error_2 (_("failed to create temporary file '%s': %s\n"),
670 h->tname, strerror(errno));
671 jnlib_free (h->tname);
672 jnlib_free (h);
673 return NULL;
674 }
675 if ( write (fd, pidstr, 11 ) != 11 )
676 goto write_failed;
677 if ( write (fd, nodename, strlen (nodename) ) != strlen (nodename) )
678 goto write_failed;
679 if ( write (fd, "\n", 1 ) != 1 )
680 goto write_failed;
681 if ( close (fd) )
682 goto write_failed;
683
684 /* Check whether we support hard links. */
685 switch (use_hardlinks_p (h->tname))
686 {
687 case 0: /* Yes. */
688 break;
689 case 1: /* No. */
690 unlink (h->tname);
691 h->use_o_excl = 1;
692 break;
693 default:
694 my_error_2 ("can't check whether hardlinks are supported for '%s': %s\n",
695 h->tname, strerror(errno));
696 goto write_failed;
697 }
698
699 h->lockname = jnlib_malloc (strlen (file_to_lock) + 6 );
700 if (!h->lockname)
701 {
702 all_lockfiles = h->next;
703 UNLOCK_all_lockfiles ();
704 unlink (h->tname);
705 jnlib_free (h->tname);
706 jnlib_free (h);
707 return NULL;
708 }
709 strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock");
710 UNLOCK_all_lockfiles ();
711 if (h->use_o_excl)
712 my_debug_1 ("locking for '%s' done via O_EXCL\n", h->lockname);
713
714 return h;
715
716 write_failed:
717 all_lockfiles = h->next;
718 UNLOCK_all_lockfiles ();
719 my_error_2 (_("error writing to '%s': %s\n"), h->tname, strerror (errno));
720 close (fd);
721 unlink (h->tname);
722 jnlib_free (h->tname);
723 jnlib_free (h);
724 return NULL;
725}
726#endif /*HAVE_POSIX_SYSTEM*/
727
728
729#ifdef HAVE_DOSISH_SYSTEM
730/* Locking core for Windows. This version does not need a temporary
731 file but uses the plain lock file along with record locking. We
732 create this file here so that we later only need to do the file
733 locking. For error reporting it is useful to keep the name of the
734 file in the handle. */
735static dotlock_t
736dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
737{
738 LOCK_all_lockfiles ();
739 h->next = all_lockfiles;
740 all_lockfiles = h;
741
742 h->lockname = jnlib_malloc ( strlen (file_to_lock) + 6 );
743 if (!h->lockname)
744 {
745 all_lockfiles = h->next;
746 UNLOCK_all_lockfiles ();
747 jnlib_free (h);
748 return NULL;
749 }
750 strcpy (stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock");
751
752 /* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE
753 along with FILE_SHARE_DELETE but that does not work due to a race
754 condition: Despite the OPEN_ALWAYS flag CreateFile may return an
755 error and we can't reliable create/open the lock file unless we
756 would wait here until it works - however there are other valid
757 reasons why a lock file can't be created and thus the process
758 would not stop as expected but spin until Windows crashes. Our
759 solution is to keep the lock file open; that does not harm. */
760 {
761#ifdef HAVE_W32CE_SYSTEM
762 wchar_t *wname = utf8_to_wchar (h->lockname);
763
764 if (wname)
765 h->lockhd = CreateFile (wname,
766 GENERIC_READ|GENERIC_WRITE,
767 FILE_SHARE_READ|FILE_SHARE_WRITE,
768 NULL, OPEN_ALWAYS, 0, NULL);
769 else
770 h->lockhd = INVALID_HANDLE_VALUE;
771 jnlib_free (wname);
772#else
773 h->lockhd = CreateFile (h->lockname,
774 GENERIC_READ|GENERIC_WRITE,
775 FILE_SHARE_READ|FILE_SHARE_WRITE,
776 NULL, OPEN_ALWAYS, 0, NULL);
777#endif
778 }
779 if (h->lockhd == INVALID_HANDLE_VALUE)
780 {
781 all_lockfiles = h->next;
782 UNLOCK_all_lockfiles ();
783 my_error_2 (_("can't create '%s': %s\n"), h->lockname, w32_strerror (-1));
784 jnlib_free (h->lockname);
785 jnlib_free (h);
786 return NULL;
787 }
788 return h;
789}
790#endif /*HAVE_DOSISH_SYSTEM*/
791
792
793/* Create a lockfile for a file name FILE_TO_LOCK and returns an
794 object of type dotlock_t which may be used later to actually acquire
795 the lock. A cleanup routine gets installed to cleanup left over
796 locks or other files used internally by the lock mechanism.
797
798 Calling this function with NULL does only install the atexit
799 handler and may thus be used to assure that the cleanup is called
800 after all other atexit handlers.
801
802 This function creates a lock file in the same directory as
803 FILE_TO_LOCK using that name and a suffix of ".lock". Note that on
804 POSIX systems a temporary file ".#lk.<hostname>.pid[.threadid] is
805 used.
806
807 FLAGS must be 0.
808
809 The function returns an new handle which needs to be released using
810 destroy_dotlock but gets also released at the termination of the
811 process. On error NULL is returned.
812 */
813
814dotlock_t
815dotlock_create (const char *file_to_lock, unsigned int flags)
816{
817 static int initialized;
818 dotlock_t h;
819
820 if ( !initialized )
821 {
822 atexit (dotlock_remove_lockfiles);
823 initialized = 1;
824 }
825
826 if ( !file_to_lock )
827 return NULL; /* Only initialization was requested. */
828
829 if (flags)
830 {
831 jnlib_set_errno (EINVAL);
832 return NULL;
833 }
834
835 h = jnlib_calloc (1, sizeof *h);
836 if (!h)
837 return NULL;
838 h->extra_fd = -1;
839
840 if (never_lock)
841 {
842 h->disable = 1;
843 LOCK_all_lockfiles ();
844 h->next = all_lockfiles;
845 all_lockfiles = h;
846 UNLOCK_all_lockfiles ();
847 return h;
848 }
849
850#ifdef HAVE_DOSISH_SYSTEM
851 return dotlock_create_w32 (h, file_to_lock);
852#else /*!HAVE_DOSISH_SYSTEM */
853 return dotlock_create_unix (h, file_to_lock);
854#endif /*!HAVE_DOSISH_SYSTEM*/
855}
856
857
858
859/* Convenience function to store a file descriptor (or any any other
860 integer value) in the context of handle H. */
861void
862dotlock_set_fd (dotlock_t h, int fd)
863{
864 h->extra_fd = fd;
865}
866
867/* Convenience function to retrieve a file descriptor (or any any other
868 integer value) stored in the context of handle H. */
869int
870dotlock_get_fd (dotlock_t h)
871{
872 return h->extra_fd;
873}
874
875
876
877#ifdef HAVE_POSIX_SYSTEM
878/* Unix specific code of destroy_dotlock. */
879static void
880dotlock_destroy_unix (dotlock_t h)
881{
882 if (h->locked && h->lockname)
883 unlink (h->lockname);
884 if (h->tname && !h->use_o_excl)
885 unlink (h->tname);
886 jnlib_free (h->tname);
887}
888#endif /*HAVE_POSIX_SYSTEM*/
889
890
891#ifdef HAVE_DOSISH_SYSTEM
892/* Windows specific code of destroy_dotlock. */
893static void
894dotlock_destroy_w32 (dotlock_t h)
895{
896 if (h->locked)
897 {
898 OVERLAPPED ovl;
899
900 memset (&ovl, 0, sizeof ovl);
901 UnlockFileEx (h->lockhd, 0, 1, 0, &ovl);
902 }
903 CloseHandle (h->lockhd);
904}
905#endif /*HAVE_DOSISH_SYSTEM*/
906
907
908/* Destroy the locck handle H and release the lock. */
909void
910dotlock_destroy (dotlock_t h)
911{
912 dotlock_t hprev, htmp;
913
914 if ( !h )
915 return;
916
917 /* First remove the handle from our global list of all locks. */
918 LOCK_all_lockfiles ();
919 for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
920 if (htmp == h)
921 {
922 if (hprev)
923 hprev->next = htmp->next;
924 else
925 all_lockfiles = htmp->next;
926 h->next = NULL;
927 break;
928 }
929 UNLOCK_all_lockfiles ();
930
931 /* Then destroy the lock. */
932 if (!h->disable)
933 {
934#ifdef HAVE_DOSISH_SYSTEM
935 dotlock_destroy_w32 (h);
936#else /* !HAVE_DOSISH_SYSTEM */
937 dotlock_destroy_unix (h);
938#endif /* HAVE_DOSISH_SYSTEM */
939 jnlib_free (h->lockname);
940 }
941 jnlib_free(h);
942}
943
944
945
946#ifdef HAVE_POSIX_SYSTEM
947/* Unix specific code of make_dotlock. Returns 0 on success and -1 on
948 error. */
949static int
950dotlock_take_unix (dotlock_t h, long timeout)
951{
952 int wtime = 0;
953 int sumtime = 0;
954 int pid;
955 int lastpid = -1;
956 int ownerchanged;
957 const char *maybe_dead="";
958 int same_node;
959
960 again:
961 if (h->use_o_excl)
962 {
963 /* No hardlink support - use open(O_EXCL). */
964 int fd;
965
966 do
967 {
968 jnlib_set_errno (0);
969 fd = open (h->lockname, O_WRONLY|O_CREAT|O_EXCL,
970 S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
971 }
972 while (fd == -1 && errno == EINTR);
973
974 if (fd == -1 && errno == EEXIST)
975 ; /* Lock held by another process. */
976 else if (fd == -1)
977 {
978 my_error_2 ("lock not made: open(O_EXCL) of '%s' failed: %s\n",
979 h->lockname, strerror (errno));
980 return -1;
981 }
982 else
983 {
984 char pidstr[16];
985
986 snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid());
987 if (write (fd, pidstr, 11 ) == 11
988 && write (fd, h->tname + h->nodename_off,h->nodename_len)
989 == h->nodename_len
990 && write (fd, "\n", 1) == 1
991 && !close (fd))
992 {
993 h->locked = 1;
994 return 0;
995 }
996 /* Write error. */
997 my_error_2 ("lock not made: writing to '%s' failed: %s\n",
998 h->lockname, strerror (errno));
999 close (fd);
1000 unlink (h->lockname);
1001 return -1;
1002 }
1003 }
1004 else /* Standard method: Use hardlinks. */
1005 {
1006 struct stat sb;
1007
1008 /* We ignore the return value of link() because it is unreliable. */
1009 (void) link (h->tname, h->lockname);
1010
1011 if (stat (h->tname, &sb))
1012 {
1013 my_error_1 ("lock not made: Oops: stat of tmp file failed: %s\n",
1014 strerror (errno));
1015 /* In theory this might be a severe error: It is possible
1016 that link succeeded but stat failed due to changed
1017 permissions. We can't do anything about it, though. */
1018 return -1;
1019 }
1020
1021 if (sb.st_nlink == 2)
1022 {
1023 h->locked = 1;
1024 return 0; /* Okay. */
1025 }
1026 }
1027
1028 /* Check for stale lock files. */
1029 if ( (pid = read_lockfile (h, &same_node)) == -1 )
1030 {
1031 if ( errno != ENOENT )
1032 {
1033 my_info_0 ("cannot read lockfile\n");
1034 return -1;
1035 }
1036 my_info_0 ("lockfile disappeared\n");
1037 goto again;
1038 }
1039 else if ( pid == getpid() && same_node )
1040 {
1041 my_info_0 ("Oops: lock already held by us\n");
1042 h->locked = 1;
1043 return 0; /* okay */
1044 }
1045 else if ( same_node && kill (pid, 0) && errno == ESRCH )
1046 {
1047 /* Note: It is unlikley that we get a race here unless a pid is
1048 reused too fast or a new process with the same pid as the one
1049 of the stale file tries to lock right at the same time as we. */
1050 my_info_1 (_("removing stale lockfile (created by %d)\n"), pid);
1051 unlink (h->lockname);
1052 goto again;
1053 }
1054
1055 if (lastpid == -1)
1056 lastpid = pid;
1057 ownerchanged = (pid != lastpid);
1058
1059 if (timeout)
1060 {
1061 struct timeval tv;
1062
1063 /* Wait until lock has been released. We use increasing retry
1064 intervals of 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s
1065 but reset it if the lock owner meanwhile changed. */
1066 if (!wtime || ownerchanged)
1067 wtime = 50;
1068 else if (wtime < 800)
1069 wtime *= 2;
1070 else if (wtime == 800)
1071 wtime = 2000;
1072 else if (wtime < 8000)
1073 wtime *= 2;
1074
1075 if (timeout > 0)
1076 {
1077 if (wtime > timeout)
1078 wtime = timeout;
1079 timeout -= wtime;
1080 }
1081
1082 sumtime += wtime;
1083 if (sumtime >= 1500)
1084 {
1085 sumtime = 0;
1086 my_info_3 (_("waiting for lock (held by %d%s) %s...\n"),
1087 pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):"");
1088 }
1089
1090
1091 tv.tv_sec = wtime / 1000;
1092 tv.tv_usec = (wtime % 1000) * 1000;
1093 select (0, NULL, NULL, NULL, &tv);
1094 goto again;
1095 }
1096
1097 jnlib_set_errno (EACCES);
1098 return -1;
1099}
1100#endif /*HAVE_POSIX_SYSTEM*/
1101
1102
1103#ifdef HAVE_DOSISH_SYSTEM
1104/* Windows specific code of make_dotlock. Returns 0 on success and -1 on
1105 error. */
1106static int
1107dotlock_take_w32 (dotlock_t h, long timeout)
1108{
1109 int wtime = 0;
1110 int w32err;
1111 OVERLAPPED ovl;
1112
1113 again:
1114 /* Lock one byte at offset 0. The offset is given by OVL. */
1115 memset (&ovl, 0, sizeof ovl);
1116 if (LockFileEx (h->lockhd, (LOCKFILE_EXCLUSIVE_LOCK
1117 | LOCKFILE_FAIL_IMMEDIATELY), 0, 1, 0, &ovl))
1118 {
1119 h->locked = 1;
1120 return 0; /* okay */
1121 }
1122
1123 w32err = GetLastError ();
1124 if (w32err != ERROR_LOCK_VIOLATION)
1125 {
1126 my_error_2 (_("lock '%s' not made: %s\n"),
1127 h->lockname, w32_strerror (w32err));
1128 return -1;
1129 }
1130
1131 if (timeout)
1132 {
1133 /* Wait until lock has been released. We use retry intervals of
1134 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s. */
1135 if (!wtime)
1136 wtime = 50;
1137 else if (wtime < 800)
1138 wtime *= 2;
1139 else if (wtime == 800)
1140 wtime = 2000;
1141 else if (wtime < 8000)
1142 wtime *= 2;
1143
1144 if (timeout > 0)
1145 {
1146 if (wtime > timeout)
1147 wtime = timeout;
1148 timeout -= wtime;
1149 }
1150
1151 if (wtime >= 800)
1152 my_info_1 (_("waiting for lock %s...\n"), h->lockname);
1153
1154 Sleep (wtime);
1155 goto again;
1156 }
1157
1158 return -1;
1159}
1160#endif /*HAVE_DOSISH_SYSTEM*/
1161
1162
1163/* Take a lock on H. A value of 0 for TIMEOUT returns immediately if
1164 the lock can't be taked, -1 waits forever (hopefully not), other
1165 values wait for TIMEOUT milliseconds. Returns: 0 on success */
1166int
1167dotlock_take (dotlock_t h, long timeout)
1168{
1169 int ret;
1170
1171 if ( h->disable )
1172 return 0; /* Locks are completely disabled. Return success. */
1173
1174 if ( h->locked )
1175 {
1176 my_debug_1 ("Oops, '%s' is already locked\n", h->lockname);
1177 return 0;
1178 }
1179
1180#ifdef HAVE_DOSISH_SYSTEM
1181 ret = dotlock_take_w32 (h, timeout);
1182#else /*!HAVE_DOSISH_SYSTEM*/
1183 ret = dotlock_take_unix (h, timeout);
1184#endif /*!HAVE_DOSISH_SYSTEM*/
1185
1186 return ret;
1187}
1188
1189
1190
1191#ifdef HAVE_POSIX_SYSTEM
1192/* Unix specific code of release_dotlock. */
1193static int
1194dotlock_release_unix (dotlock_t h)
1195{
1196 int pid, same_node;
1197
1198 pid = read_lockfile (h, &same_node);
1199 if ( pid == -1 )
1200 {
1201 my_error_0 ("release_dotlock: lockfile error\n");
1202 return -1;
1203 }
1204 if ( pid != getpid() || !same_node )
1205 {
1206 my_error_1 ("release_dotlock: not our lock (pid=%d)\n", pid);
1207 return -1;
1208 }
1209
1210 if ( unlink( h->lockname ) )
1211 {
1212 my_error_1 ("release_dotlock: error removing lockfile '%s'\n",
1213 h->lockname);
1214 return -1;
1215 }
1216 /* Fixme: As an extra check we could check whether the link count is
1217 now really at 1. */
1218 return 0;
1219}
1220#endif /*HAVE_POSIX_SYSTEM */
1221
1222
1223#ifdef HAVE_DOSISH_SYSTEM
1224/* Windows specific code of release_dotlock. */
1225static int
1226dotlock_release_w32 (dotlock_t h)
1227{
1228 OVERLAPPED ovl;
1229
1230 memset (&ovl, 0, sizeof ovl);
1231 if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl))
1232 {
1233 my_error_2 ("release_dotlock: error removing lockfile '%s': %s\n",
1234 h->lockname, w32_strerror (-1));
1235 return -1;
1236 }
1237
1238 return 0;
1239}
1240#endif /*HAVE_DOSISH_SYSTEM */
1241
1242
1243/* Release a lock. Returns 0 on success. */
1244int
1245dotlock_release (dotlock_t h)
1246{
1247 int ret;
1248
1249 /* To avoid atexit race conditions we first check whether there are
1250 any locks left. It might happen that another atexit handler
1251 tries to release the lock while the atexit handler of this module
1252 already ran and thus H is undefined. */
1253 LOCK_all_lockfiles ();
1254 ret = !all_lockfiles;
1255 UNLOCK_all_lockfiles ();
1256 if (ret)
1257 return 0;
1258
1259 if ( h->disable )
1260 return 0;
1261
1262 if ( !h->locked )
1263 {
1264 my_debug_1 ("Oops, '%s' is not locked\n", h->lockname);
1265 return 0;
1266 }
1267
1268#ifdef HAVE_DOSISH_SYSTEM
1269 ret = dotlock_release_w32 (h);
1270#else
1271 ret = dotlock_release_unix (h);
1272#endif
1273
1274 if (!ret)
1275 h->locked = 0;
1276 return ret;
1277}
1278
1279
1280
1281/* Remove all lockfiles. This is called by the atexit handler
1282 installed by this module but may also be called by other
1283 termination handlers. */
1284void
1285dotlock_remove_lockfiles (void)
1286{
1287 dotlock_t h, h2;
1288
1289 /* First set the lockfiles list to NULL so that for example
1290 dotlock_release is ware that this fucntion is currently
1291 running. */
1292 LOCK_all_lockfiles ();
1293 h = all_lockfiles;
1294 all_lockfiles = NULL;
1295 UNLOCK_all_lockfiles ();
1296
1297 while ( h )
1298 {
1299 h2 = h->next;
1300 dotlock_destroy (h);
1301 h = h2;
1302 }
1303}
diff --git a/dotlock.h b/dotlock.h
new file mode 100644
index 0000000..3fb9bcb
--- /dev/null
+++ b/dotlock.h
@@ -0,0 +1,112 @@
1/* dotlock.h - dotfile locking declarations
2 * Copyright (C) 2000, 2001, 2006, 2011 Free Software Foundation, Inc.
3 *
4 * This file is part of JNLIB, which is a subsystem of GnuPG.
5 *
6 * JNLIB is free software; you can redistribute it and/or modify it
7 * under the terms of either
8 *
9 * - the GNU Lesser General Public License as published by the Free
10 * Software Foundation; either version 3 of the License, or (at
11 * your option) any later version.
12 *
13 * or
14 *
15 * - the GNU General Public License as published by the Free
16 * Software Foundation; either version 2 of the License, or (at
17 * your option) any later version.
18 *
19 * or both in parallel, as here.
20 *
21 * JNLIB is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * General Public License for more details.
25 *
26 * You should have received a copies of the GNU General Public License
27 * and the GNU Lesser General Public License along with this program;
28 * if not, see <http://www.gnu.org/licenses/>.
29 *
30 * ALTERNATIVELY, this file may be distributed under the terms of the
31 * following license, in which case the provisions of this license are
32 * required INSTEAD OF the GNU Lesser General License or the GNU
33 * General Public License. If you wish to allow use of your version of
34 * this file only under the terms of the GNU Lesser General License or
35 * the GNU General Public License, and not to allow others to use your
36 * version of this file under the terms of the following license,
37 * indicate your decision by deleting this paragraph and the license
38 * below.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 *
44 * 1. Redistributions of source code must retain the above copyright
45 * notice, and the entire permission notice in its entirety,
46 * including the disclaimer of warranties.
47 * 2. Redistributions in binary form must reproduce the above copyright
48 * notice, this list of conditions and the following disclaimer in the
49 * documentation and/or other materials provided with the distribution.
50 * 3. The name of the author may not be used to endorse or promote
51 * products derived from this software without specific prior
52 * written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
55 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
56 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
57 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
58 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
59 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
60 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
62 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
63 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
64 * OF THE POSSIBILITY OF SUCH DAMAGE.
65 */
66
67#ifndef LIBJNLIB_DOTLOCK_H
68#define LIBJNLIB_DOTLOCK_H
69
70/* See dotlock.c for a description. */
71
72#ifdef DOTLOCK_EXT_SYM_PREFIX
73# ifndef _DOTLOCK_PREFIX
74# define _DOTLOCK_PREFIX1(x,y) x ## y
75# define _DOTLOCK_PREFIX2(x,y) _DOTLOCK_PREFIX1(x,y)
76# define _DOTLOCK_PREFIX(x) _DOTLOCK_PREFIX2(DOTLOCK_EXT_SYM_PREFIX,x)
77# endif /*_DOTLOCK_PREFIX*/
78# define dotlock_disable _DOTLOCK_PREFIX(dotlock_disable)
79# define dotlock_create _DOTLOCK_PREFIX(dotlock_create)
80# define dotlock_set_fd _DOTLOCK_PREFIX(dotlock_set_fd)
81# define dotlock_get_fd _DOTLOCK_PREFIX(dotlock_get_fd)
82# define dotlock_destroy _DOTLOCK_PREFIX(dotlock_destroy)
83# define dotlock_take _DOTLOCK_PREFIX(dotlock_take)
84# define dotlock_release _DOTLOCK_PREFIX(dotlock_release)
85# define dotlock_remove_lockfiles _DOTLOCK_PREFIX(dotlock_remove_lockfiles)
86#endif /*DOTLOCK_EXT_SYM_PREFIX*/
87
88#ifdef __cplusplus
89extern "C"
90{
91#if 0
92}
93#endif
94#endif
95
96
97struct dotlock_handle;
98typedef struct dotlock_handle *dotlock_t;
99
100void dotlock_disable (void);
101dotlock_t dotlock_create (const char *file_to_lock, unsigned int flags);
102void dotlock_set_fd (dotlock_t h, int fd);
103int dotlock_get_fd (dotlock_t h);
104void dotlock_destroy (dotlock_t h);
105int dotlock_take (dotlock_t h, long timeout);
106int dotlock_release (dotlock_t h);
107void dotlock_remove_lockfiles (void);
108
109#ifdef __cplusplus
110}
111#endif
112#endif /*LIBJNLIB_DOTLOCK_H*/
diff --git a/kiki.cabal b/kiki.cabal
index 1fd9507..fbf9470 100644
--- a/kiki.cabal
+++ b/kiki.cabal
@@ -22,3 +22,4 @@ Executable kiki
22 unix, time, crypto-api, cryptocipher (>=0.3.7), 22 unix, time, crypto-api, cryptocipher (>=0.3.7),
23 containers -any 23 containers -any
24 ghc-options: -O2 24 ghc-options: -O2
25 c-sources: dotlock.c