Allow use on systems which return ENOSYS for sem_init.
authorWerner Koch <wk@gnupg.org>
Wed, 29 Oct 2014 11:25:20 +0000 (12:25 +0100)
committerWerner Koch <wk@gnupg.org>
Wed, 29 Oct 2014 11:25:20 +0000 (12:25 +0100)
* src/npth.c: Include some more headers.
(sceptre_buffer): New.
(sceptre): Change to a pointer and init to sceptre_buffer.
(enter_npth, leave_npth): Adjust.
(try_sem_open): New.
(npth_init): Check for ENOSYS and use sem_open.
--

This is for example the case on OS X and some AIX versions Thanks to
Patrick Brunschwig for finding this problem and suggesting a solution.

src/npth.c

index d9e2801..2fc6ba3 100644 (file)
 #endif
 
 #include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
 #include <assert.h>
 #include <errno.h>
 #include <pthread.h>
+#include <fcntl.h>
+#include <sys/stat.h>
 #include <semaphore.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
    the application or other libraries call fork(), including from a
    signal handler.  sem_post is async-signal-safe.  (The reason a
    semaphore is safe and a mutex is not safe is that a mutex has an
-   owner, while a semaphore does not.)  */
-static sem_t sceptre;
+   owner, while a semaphore does not.)  We init sceptre to a static
+   buffer for use by sem_init; in case sem_open is used instead
+   SCEPTRE will changed to the value returned by sem_open.  */
+static sem_t sceptre_buffer;
+static sem_t *sceptre = &sceptre_buffer;
 
 /* The main thread is the active thread at the time pth_init was
    called.  As of now it is only useful for debugging.  The volatile
@@ -112,7 +119,7 @@ enter_npth (void)
 {
   int res;
 
-  res = sem_post (&sceptre);
+  res = sem_post (sceptre);
   assert (res == 0);
 }
 
@@ -123,7 +130,7 @@ leave_npth (void)
   int res;
 
   do {
-    res = sem_wait (&sceptre);
+    res = sem_wait (sceptre);
   } while (res < 0 && errno == EINTR);
 
   assert (!res);
@@ -133,6 +140,34 @@ leave_npth (void)
 #define LEAVE() leave_npth ()
 
 
+static int
+try_sem_open (sem_t **r_sem)
+{
+  sem_t *sem;
+  char name [256];
+  int counter = 0;
+
+  do
+    {
+      snprintf (name, sizeof name - 1, "/npth-sceptre-%lu-%u",
+                (unsigned long)getpid (), counter);
+      name[sizeof name -1] = 0;
+      counter++;
+
+      sem = sem_open (name, (O_CREAT | O_EXCL), (S_IRUSR | S_IWUSR), 1);
+      if (sem != SEM_FAILED)
+        {
+          *r_sem = sem;
+          return 0;
+        }
+      fprintf (stderr, " semOpen(%s): %s\n", name, strerror (errno));
+    }
+  while (errno == EEXIST);
+
+  return -1;
+}
+
+
 int
 npth_init (void)
 {
@@ -140,16 +175,32 @@ npth_init (void)
 
   main_thread = pthread_self();
 
+  /* Better reset ERRNO so that we know that it has been set by
+     sem_init.  */
+  errno = 0;
+
   /* The semaphore is not shared and binary.  */
-  res = sem_init(&sceptre, 0, 1);
+  res = sem_init (sceptre, 0, 1);
   if (res < 0)
     {
-      /* POSIX.1-2001 defines the semaphore interface but does not
-         specify the return value for success.  Thus we better bail
-         out on error only on a POSIX.1-2008 system.  */
+      /* Mac OSX and some AIX versions have sem_init but return
+         ENOSYS.  This is allowed according to some POSIX versions but
+         the informative section is quite fuzzy about it.  We resort
+         to sem_open in this case.  */
+      if (errno == ENOSYS)
+        {
+          if (try_sem_open (&sceptre))
+            return errno;
+        }
+      else
+        {
+          /* POSIX.1-2001 defines the semaphore interface but does not
+             specify the return value for success.  Thus we better
+             bail out on error only on a POSIX.1-2008 system.  */
 #if _POSIX_C_SOURCE >= 200809L
-      return errno;
+          return errno;
 #endif
+        }
     }
 
   LEAVE();