]> granicus.if.org Git - nethack/commitdiff
Implement fcntl(2) file locking
authorPasi Kallinen <paxed@alt.org>
Sat, 7 Mar 2015 13:11:41 +0000 (15:11 +0200)
committerPasi Kallinen <paxed@alt.org>
Sat, 7 Mar 2015 13:11:41 +0000 (15:11 +0200)
If you run a server, then you know of the somewhat annoying perm_lock
errors that creep up, requiring your attention before anyone else can
start a game.

This patch properly implements fcntl(2) locking on systems that can
handle it (*nix systems), that results in the lock being automatically
released on program termination, whether abnormal or not.

Original patch by Drew Streib, update by Edoardo Spadolini

include/unixconf.h
src/files.c

index c85611c2a0a59b00a50d80ac2053de2d6d262129..9feefd2c8e1e861aa67fc219867d3f8de5116663 100644 (file)
 
 #define FCMASK 0660    /* file creation mask */
 
+/* fcntl(2) is a POSIX-portable call for manipulating file descriptors.
+ * Comment out the USE_FCNTL if for some reason you have a strange
+ * OS/filesystem combination for which fcntl(2) does not work. */
+#ifdef POSIX_TYPES
+# define USE_FCNTL
+#endif
 
 /*
  * The remainder of the file should not need to be changed.
index 71fd0c21730b265a6b425c2e3225db348bdbb51f..2cae9fb7f80515a7c5430111d24184d6808c1ac2 100644 (file)
@@ -12,7 +12,7 @@
 
 #include <ctype.h>
 
-#if !defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)
+#if (!defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)) || defined(USE_FCNTL)
 #include <fcntl.h>
 #endif
 
@@ -185,7 +185,9 @@ STATIC_DCL void FDECL(docompress_file, (const char *,BOOLEAN_P));
 #if defined(ZLIB_COMP)
 STATIC_DCL boolean FDECL(make_compressed_name, (const char *, char *));
 #endif
+#ifndef USE_FCNTL
 STATIC_DCL char *FDECL(make_lockname, (const char *,char *));
+#endif
 STATIC_DCL FILE *FDECL(fopen_config_file, (const char *, int));
 STATIC_DCL int FDECL(get_uchars,
                    (FILE *,char *,char *,uchar *,BOOLEAN_P,int,const char *));
@@ -1537,12 +1539,16 @@ boolean uncomp;
 
 static int nesting = 0;
 
-#ifdef NO_FILE_LINKS   /* implies UNIX */
+#if defined(NO_FILE_LINKS) || defined(USE_FCNTL)       /* implies UNIX */
 static int lockfd;     /* for lock_file() to pass to unlock_file() */
 #endif
+#ifdef USE_FCNTL
+struct flock sflock; /* for unlocking, same as above */
+#endif
 
 #define HUP    if (!program_state.done_hup)
 
+#ifndef USE_FCNTL
 STATIC_OVL char *
 make_lockname(filename, lockname)
 const char *filename;
@@ -1574,6 +1580,7 @@ char *lockname;
        return (char*)0;
 #endif
 }
+#endif /* !USE_FCNTL */
 
 /* lock a file */
 boolean
@@ -1585,8 +1592,10 @@ int retryct;
 #if defined(PRAGMA_UNUSED) && !(defined(UNIX) || defined(VMS)) && !(defined(AMIGA) || defined(WIN32) || defined(MSDOS))
 # pragma unused(retryct)
 #endif
+#ifndef USE_FCNTL
        char locknambuf[BUFSZ];
        const char *lockname;
+#endif
 
        nesting++;
        if (nesting > 1) {
@@ -1594,18 +1603,50 @@ int retryct;
            return TRUE;
        }
 
+#ifndef USE_FCNTL
        lockname = make_lockname(filename, locknambuf);
-       filename = fqname(filename, whichprefix, 0);
 #ifndef NO_FILE_LINKS  /* LOCKDIR should be subsumed by LOCKPREFIX */
        lockname = fqname(lockname, LOCKPREFIX, 2);
 #endif
+#endif
+       filename = fqname(filename, whichprefix, 0);
+#ifdef USE_FCNTL
+       lockfd = open(filename,O_RDWR);
+       if (lockfd == -1) {
+               HUP raw_printf("Cannot open file %s. This is a program bug.",
+                       filename);
+       }
+       sflock.l_type = F_WRLCK;
+       sflock.l_whence = SEEK_SET;
+       sflock.l_start = 0;
+       sflock.l_len = 0;
+#endif
 
 #if defined(UNIX) || defined(VMS)
+# ifdef USE_FCNTL
+       while (fcntl(lockfd,F_SETLK,&sflock) == -1) {
+# else
 # ifdef NO_FILE_LINKS
        while ((lockfd = open(lockname, O_RDWR|O_CREAT|O_EXCL, 0666)) == -1) {
 # else
        while (link(filename, lockname) == -1) {
 # endif
+# endif
+
+#ifdef USE_FCNTL
+           if (retryct--) {
+               HUP raw_printf(
+                              "Waiting for release of fcntl lock on %s. (%d retries left).",
+                              filename, retryct);
+               sleep(1);
+           } else {
+               HUP (void) raw_print("I give up.  Sorry.");
+               HUP raw_printf("Some other process has an unnatural grip on %s.",
+                              filename);
+               nesting--;
+               return FALSE;
+           }
+#else
            register int errnosv = errno;
 
            switch (errnosv) {  /* George Barbanis */
@@ -1660,11 +1701,12 @@ int retryct;
                nesting--;
                return FALSE;
            }
+#endif /* USE_FCNTL */
 
        }
 #endif  /* UNIX || VMS */
 
-#if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
+#if (defined(AMIGA) || defined(WIN32) || defined(MSDOS)) && !defined(USE_FCNTL)
 # ifdef AMIGA
 #define OPENFAILURE(fd) (!fd)
     lockptr = 0;
@@ -1711,10 +1753,19 @@ void
 unlock_file(filename)
 const char *filename;
 {
+#ifndef USE_FCNTL
        char locknambuf[BUFSZ];
        const char *lockname;
+#endif
 
        if (nesting == 1) {
+#ifdef USE_FCNTL
+               sflock.l_type = F_UNLCK;
+               if (fcntl(lockfd,F_SETLK,&sflock) == -1) {
+                       HUP raw_printf("Can't remove fcntl lock on %s.", filename);
+                       (void) close(lockfd);
+               }
+#else
                lockname = make_lockname(filename, locknambuf);
 #ifndef NO_FILE_LINKS  /* LOCKDIR should be subsumed by LOCKPREFIX */
                lockname = fqname(lockname, LOCKPREFIX, 2);
@@ -1734,6 +1785,7 @@ const char *filename;
                DeleteFile(lockname);
                lockptr = 0;
 #endif /* AMIGA || WIN32 || MSDOS */
+#endif /* USE_FCNTL */
        }
 
        nesting--;