From 8a033fecfb4c73b8f7d1119272c10e64d9176bcb Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 27 Sep 2011 17:18:56 +0200 Subject: [PATCH] Improved the dotlock module. - It is now more portable and may be used outside of GnuPG - vfat file systems are now supported. - The use of link(2) is more robust. - Wrote extensive documentation. --- common/ChangeLog | 8 + common/dotlock.c | 413 ++++++++++++++++++++++++++++++++++++++++----- common/dotlock.h | 6 +- common/t-dotlock.c | 145 ++++++++++++++++ 4 files changed, 529 insertions(+), 43 deletions(-) create mode 100644 common/t-dotlock.c diff --git a/common/ChangeLog b/common/ChangeLog index 7d803660a..e0fc2dddc 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,11 @@ +2011-09-27 Werner Koch + + * dotlock.c (dotlock_take_unix): Check only the link count and not + the error return from link. + (use_hardlinks_p): New. + (dotlock_create_unix): Test for hardlinks. + (dotlock_take_unix): Implement O_EXCL locking. + 2011-09-23 Werner Koch * dotlock.c: Factor Unix and W32 specific code out into specific diff --git a/common/dotlock.c b/common/dotlock.c index 7d0ac1fab..97a3f7462 100644 --- a/common/dotlock.c +++ b/common/dotlock.c @@ -18,6 +18,172 @@ * License along with this program; if not, see . */ +/* + 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: + + dotlock_create (NULL); + + This installs an atexit handler and may also initialize mutex etc. + It is optional for non-threaded applications. Only the first call + has an effect. + + To create a lock file (which prepares it but does not take the + lock) you do: + + dotlock_t h + + h = dotlock_create (fname); + 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 + name with the suffix ".lock" appended. This call creates a unique + file temporary file (".#lk*") in the same directory as FNAME and + returns a handle for further operations. The module keeps track of + theses unique files so that they will be unlinked using the atexit + handler. If you don't need the lock file anymore, you may also + explicitly remove it with a call to: + + 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 + you pass (0) instead of (-1) the function does not wait if the file + is already locked but returns -1 and sets ERRNO to EACCES. + + To release the lock you call: + + if (dotlock_release (h)) + error ("error releasing lock: %s\n", strerror (errno)); + + or, if the lock file is not anymore needed, you may call + dotlock_destroy. + + 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. + + + 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: + + DOTLOCK_GLIB_LOGGING - Define this to use Glib logging functions. + + GNUPG_MAJOR_VERSION - Defined when used by GnuPG. + + 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. + + HAVE_SIGNAL_H - Should be defined on Posix systems. If config.h + is not used defaults to defined. + + DIRSEP_C - Separation character for file name parts. + Usually not redefined. + EXTSEP_S "." - Separation string for file name suffixes. + Usually not redefined. + + HAVE_W32CE_SYSTEM - Currently only used by GnuPG. + + 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 + + + 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. + +*/ + #ifdef HAVE_CONFIG_H # include #endif @@ -31,9 +197,14 @@ # define HAVE_POSIX_SYSTEM 1 #endif +/* 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 /* Standard headers. */ #include +#include #include #include #include @@ -43,6 +214,8 @@ # define WIN32_LEAN_AND_MEAN /* We only need the OS core stuff. */ # include #else +# include +# include # include #endif #include @@ -53,14 +226,19 @@ # include #endif +#ifdef DOTLOCK_GLIB_LOGGING +# include +#endif -#include "libjnlib-config.h" -#include "stringhelp.h" -#include "dotlock.h" +#ifdef GNUPG_MAJOR_VERSION +# include "libjnlib-config.h" +#endif #ifdef HAVE_W32CE_SYSTEM # include "utf8conv.h" /* WindowsCE requires filename conversion. */ #endif +#include "dotlock.h" + /* Define constants for file name construction. */ #if !defined(DIRSEP_C) && !defined(EXTSEP_S) @@ -91,15 +269,52 @@ # endif #endif +/* 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)) +#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)) +# define my_error_2(a,b,c g_warning ((a), (b), (c)) +# define my_debug_1(a,b) g_debug ((a), (b)) +#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)) +#endif + + + /* The object describing a lock. */ struct dotlock_handle { struct dotlock_handle *next; - char *lockname; /* Name of the actual lockfile. */ - int locked; /* Lock status. */ - int disable; /* If true, locking is disabled. */ + 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. */ #ifdef HAVE_DOSISH_SYSTEM HANDLE lockhd; /* The W32 handle of the lock file. */ @@ -175,8 +390,8 @@ read_lockfile (dotlock_t h, int *same_node ) if ( (fd = open (h->lockname, O_RDONLY)) == -1 ) { int e = errno; - log_info ("error opening lockfile `%s': %s\n", - h->lockname, strerror(errno) ); + my_info_2 ("error opening lockfile `%s': %s\n", + h->lockname, strerror(errno) ); if (buffer != buffer_space) jnlib_free (buffer); jnlib_set_errno (e); /* Need to return ERRNO here. */ @@ -192,7 +407,7 @@ read_lockfile (dotlock_t h, int *same_node ) continue; if (res < 0) { - log_info ("error reading lockfile `%s'", h->lockname ); + my_info_1 ("error reading lockfile `%s'\n", h->lockname ); close (fd); if (buffer != buffer_space) jnlib_free (buffer); @@ -207,7 +422,7 @@ read_lockfile (dotlock_t h, int *same_node ) if (nread < 11) { - log_info ("invalid size of lockfile `%s'", h->lockname ); + my_info_1 ("invalid size of lockfile `%s'\n", h->lockname); if (buffer != buffer_space) jnlib_free (buffer); jnlib_set_errno (0); /* Better don't return an inappropriate ERRNO. */ @@ -218,7 +433,7 @@ read_lockfile (dotlock_t h, int *same_node ) || (buffer[10] = 0, pid = atoi (buffer)) == -1 || !pid ) { - log_error ("invalid pid %d in lockfile `%s'", pid, h->lockname ); + my_error_2 ("invalid pid %d in lockfile `%s'\n", pid, h->lockname); if (buffer != buffer_space) jnlib_free (buffer); jnlib_set_errno (0); @@ -237,6 +452,46 @@ read_lockfile (dotlock_t h, int *same_node ) #endif /*HAVE_POSIX_SYSTEM */ +/* 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; + + lname = jnlib_malloc (strlen (tname) + 1 + 1); + if (!lname) + return -1; + strcpy (lname, tname); + strcat (lname, "x"); + + link (tname, lname); + + 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. */ + + unlink (lname); + jnlib_free (lname); + return res; +} +#endif /*HAVE_POSIX_SYSTEM */ + + #ifdef HAVE_POSIX_SYSTEM /* Locking core for Unix. It used a temporary file and the link @@ -277,7 +532,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock) h->next = all_lockfiles; all_lockfiles = h; - tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10; + tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10 + 1; h->tname = jnlib_malloc (tnamelen + 1); if (!h->tname) { @@ -303,7 +558,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock) if ( fd == -1 ) { all_lockfiles = h->next; - log_error (_("failed to create temporary file `%s': %s\n"), + my_error_2 (_("failed to create temporary file `%s': %s\n"), h->tname, strerror(errno)); jnlib_free (h->tname); jnlib_free (h); @@ -318,10 +573,25 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock) if ( close (fd) ) goto write_failed; + /* 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: + my_error_2 ("can't check whether hardlinks are supported for `%s': %s\n", + h->tname, strerror(errno)); + goto write_failed; + } + # ifdef _REENTRANT /* release mutex */ # endif - h->lockname = jnlib_malloc ( strlen (file_to_lock) + 6 ); + h->lockname = jnlib_malloc (strlen (file_to_lock) + 6 ); if (!h->lockname) { all_lockfiles = h->next; @@ -331,6 +601,9 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock) return NULL; } strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock"); + if (h->use_o_excl) + my_debug_1 ("locking for `%s' done via O_EXCL\n", h->lockname); + return h; write_failed: @@ -338,7 +611,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock) # ifdef _REENTRANT /* fixme: release mutex */ # endif - log_error ( _("error writing to `%s': %s\n"), h->tname, strerror(errno) ); + my_error_2 (_("error writing to `%s': %s\n"), h->tname, strerror (errno)); close (fd); unlink (h->tname); jnlib_free (h->tname); @@ -396,7 +669,7 @@ dotlock_create_w32 (dotlock_t h, const char *file_to_lock) } if (h->lockhd == INVALID_HANDLE_VALUE) { - log_error (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1)); + my_error_2 (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1)); all_lockfiles = h->next; jnlib_free (h->lockname); jnlib_free (h); @@ -471,7 +744,7 @@ dotlock_destroy_unix (dotlock_t h) { if (h->locked && h->lockname) unlink (h->lockname); - if (h->tname) + if (h->tname && !h->use_o_excl) unlink (h->tname); jnlib_free (h->tname); } @@ -541,37 +814,95 @@ dotlock_take_unix (dotlock_t h, long timeout, int *backoff) const char *maybe_dead=""; int same_node; - if ( !link(h->tname, h->lockname) ) + if (h->use_o_excl) { - /* fixme: better use stat to check the link count */ - h->locked = 1; - return 0; /* okay */ + /* No hardlink support - use open(O_EXCL). */ + int fd; + + do + { + jnlib_set_errno (0); + 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) + { + my_error_2 ("lock not made: open(O_EXCL) of `%s' failed: %s\n", + h->lockname, strerror (errno)); + return -1; + } + else + { + char pidstr[16]; + + snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid()); + 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. */ + my_error_2 ("lock not made: writing to `%s' failed: %s\n", + h->lockname, strerror (errno)); + close (fd); + unlink (h->lockname); + return -1; + } } - if ( errno != EEXIST ) + else /* Standard method: Use hardlinks. */ { - log_error ( "lock not made: link() failed: %s\n", strerror(errno) ); - return -1; + struct stat sb; + + link (h->tname, h->lockname); + + if (stat (h->tname, &sb)) + { + my_error_1 ("lock not made: Oops: stat of tmp file failed: %s\n", + strerror (errno)); + /* 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. */ + return -1; + } + + if (sb.st_nlink == 2) + { + h->locked = 1; + return 0; /* Okay. */ + } } + /* Check for stale lock files. */ if ( (pid = read_lockfile (h, &same_node)) == -1 ) { if ( errno != ENOENT ) { - log_info ("cannot read lockfile\n"); + my_info_0 ("cannot read lockfile\n"); return -1; } - log_info( "lockfile disappeared\n"); + my_info_0 ("lockfile disappeared\n"); return 1; /* Try again. */ } else if ( pid == getpid() && same_node ) { - log_info( "Oops: lock already held by us\n"); + my_info_0 ("Oops: lock already held by us\n"); h->locked = 1; return 0; /* okay */ } else if ( same_node && kill (pid, 0) && errno == ESRCH ) { - log_info (_("removing stale lockfile (created by %d)\n"), pid ); + /* Note: It is unlikley that we get a race here unless a pid is + reused too fast or a new process with the same pid as the one + of the stale file tries to lock right at the same time as we. */ + my_info_1 (_("removing stale lockfile (created by %d)\n"), pid); unlink (h->lockname); return 1; /* Try again. */ } @@ -581,8 +912,8 @@ dotlock_take_unix (dotlock_t h, long timeout, int *backoff) /* Wait until lock has been released. */ struct timeval tv; - log_info (_("waiting for lock (held by %d%s) %s...\n"), - pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):""); + my_info_3 (_("waiting for lock (held by %d%s) %s...\n"), + pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):""); /* We can't use sleep, cause signals may be blocked. */ tv.tv_sec = 1 + *backoff; @@ -620,15 +951,15 @@ dotlock_take_w32 (dotlock_t h, long timeout, int *backoff) w32err = GetLastError (); if (w32err != ERROR_LOCK_VIOLATION) { - log_error (_("lock `%s' not made: %s\n"), - h->lockname, w32_strerror (w32err)); + my_error_2 (_("lock `%s' not made: %s\n"), + h->lockname, w32_strerror (w32err)); return -1; } if ( timeout == -1 ) { /* Wait until lock has been released. */ - log_info (_("waiting for lock %s...\n"), h->lockname); + my_info_1 (_("waiting for lock %s...\n"), h->lockname); Sleep ((1 + *backoff)*1000); if ( *backoff < 10 ) ++*backoff; @@ -655,7 +986,7 @@ dotlock_take (dotlock_t h, long timeout) if ( h->locked ) { - log_debug ("Oops, `%s' is already locked\n", h->lockname); + my_debug_1 ("Oops, `%s' is already locked\n", h->lockname); return 0; } @@ -684,19 +1015,19 @@ dotlock_release_unix (dotlock_t h) pid = read_lockfile (h, &same_node); if ( pid == -1 ) { - log_error( "release_dotlock: lockfile error\n"); + my_error_0 ("release_dotlock: lockfile error\n"); return -1; } if ( pid != getpid() || !same_node ) { - log_error( "release_dotlock: not our lock (pid=%d)\n", pid); + my_error_1 ("release_dotlock: not our lock (pid=%d)\n", pid); return -1; } if ( unlink( h->lockname ) ) { - log_error ("release_dotlock: error removing lockfile `%s'\n", - h->lockname); + my_error_1 ("release_dotlock: error removing lockfile `%s'\n", + h->lockname); return -1; } /* Fixme: As an extra check we could check whether the link count is @@ -716,8 +1047,8 @@ dotlock_release_w32 (dotlock_t h) memset (&ovl, 0, sizeof ovl); if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl)) { - log_error ("release_dotlock: error removing lockfile `%s': %s\n", - h->lockname, w32_strerror (-1)); + my_error_2 ("release_dotlock: error removing lockfile `%s': %s\n", + h->lockname, w32_strerror (-1)); return -1; } @@ -744,7 +1075,7 @@ dotlock_release (dotlock_t h) if ( !h->locked ) { - log_debug("Oops, `%s' is not locked\n", h->lockname); + my_debug_1 ("Oops, `%s' is not locked\n", h->lockname); return 0; } diff --git a/common/dotlock.h b/common/dotlock.h index 276a6cd0f..5fb7891fa 100644 --- a/common/dotlock.h +++ b/common/dotlock.h @@ -1,5 +1,5 @@ -/* dotlock.h - dotfile locking - * Copyright (C) 2000, 2001, 2006, 2011 Free Software Foundation, Inc. +/* dotlock.h - dotfile locking declarations + * Copyright (C) 2000, 2001, 2006, 2011 Free Software Foundation, Inc. * * This file is part of JNLIB, which is a subsystem of GnuPG. * @@ -20,6 +20,8 @@ #ifndef LIBJNLIB_DOTLOCK_H #define LIBJNLIB_DOTLOCK_H +/* See dotlock.c for a description. */ + struct dotlock_handle; typedef struct dotlock_handle *dotlock_t; diff --git a/common/t-dotlock.c b/common/t-dotlock.c new file mode 100644 index 000000000..a352f6e95 --- /dev/null +++ b/common/t-dotlock.c @@ -0,0 +1,145 @@ +/* t-dotlock.c - Module test for dotlock.c + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* Note: This is a standalone test program which does not rely on any + GnuPG helper files. However, it may also be build as part of the + GnuPG build system. */ + +#ifdef HAVE_CONFIG_H +# include +#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 + +#include +#include +#include +#include +#include +#include +#include + +#include "dotlock.h" + +#define PGM "t-dotlock" + + +static volatile int ctrl_c_pending; + +static void +control_c_handler (int signo) +{ + (void)signo; + ctrl_c_pending = 1; +} + + + +static void +die (const char *format, ...) +{ + va_list arg_ptr; + + va_start (arg_ptr, format); + fprintf (stderr, PGM "[%lu]: ", (unsigned long)getpid ()); + vfprintf (stderr, format, arg_ptr); + putc ('\n', stderr); + va_end (arg_ptr); + exit (1); +} + + +static void +inf (const char *format, ...) +{ + va_list arg_ptr; + + va_start (arg_ptr, format); + fprintf (stderr, PGM "[%lu]: ", (unsigned long)getpid ()); + vfprintf (stderr, format, arg_ptr); + putc ('\n', stderr); + va_end (arg_ptr); +} + + +static void +lock_and_unlock (const char *fname) +{ + dotlock_t h; + + h = dotlock_create (fname); + if (!h) + die ("error creating lock file for `%s': %s", fname, strerror (errno)); + inf ("lock created"); + + while (!ctrl_c_pending) + { + if (dotlock_take (h, -1)) + die ("error taking lock"); + inf ("lock taken"); + sleep (1); + if (dotlock_release (h)) + die ("error releasing lock"); + inf ("lock released"); + sleep (1); + } + dotlock_destroy (h); + inf ("lock destroyed"); +} + + +int +main (int argc, char **argv) +{ + const char *fname; + + if (argc > 1) + fname = argv[1]; + else + fname = "t-dotlock.tmp"; + + { + struct sigaction nact; + + nact.sa_handler = control_c_handler; + nact.sa_flags = 0; + sigaction (SIGINT, &nact, NULL); + } + + dotlock_create (NULL); /* Initialize (optional). */ + + lock_and_unlock (fname); + + + return 0; +} + + +/* +Local Variables: +compile-command: "cc -Wall -O2 -D_FILE_OFFSET_BITS=64 -o t-dotlock t-dotlock.c dotlock.c" +End: +*/