1221 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1221 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C++
		
	
	
	
/* posix_ipc.cc: POSIX IPC API for Cygwin.
 | 
						|
 | 
						|
   Copyright 2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc.
 | 
						|
 | 
						|
This file is part of Cygwin.
 | 
						|
 | 
						|
This software is a copyrighted work licensed under the terms of the
 | 
						|
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | 
						|
details. */
 | 
						|
 | 
						|
#include "winsup.h"
 | 
						|
#include "shared_info.h"
 | 
						|
#include "thread.h"
 | 
						|
#include "path.h"
 | 
						|
#include "cygtls.h"
 | 
						|
#include "fhandler.h"
 | 
						|
#include "dtable.h"
 | 
						|
#include "cygheap.h"
 | 
						|
#include "sigproc.h"
 | 
						|
#include "ntdll.h"
 | 
						|
#include <sys/mman.h>
 | 
						|
#include <sys/param.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <mqueue.h>
 | 
						|
#include <semaphore.h>
 | 
						|
 | 
						|
/* The prefix_len is the length of the path prefix ncluding trailing "/"
 | 
						|
   (or "/sem." for semaphores) as well as the trailing NUL. */
 | 
						|
static struct
 | 
						|
{
 | 
						|
  const char *prefix;
 | 
						|
  const size_t prefix_len;
 | 
						|
  const char *description;
 | 
						|
} ipc_names[] = {
 | 
						|
  { "/dev/shm", 10, "POSIX shared memory object" },
 | 
						|
  { "/dev/mqueue", 13, "POSIX message queue" },
 | 
						|
  { "/dev/shm", 14, "POSIX semaphore" }
 | 
						|
};
 | 
						|
 | 
						|
enum ipc_type_t
 | 
						|
{
 | 
						|
  shmem,
 | 
						|
  mqueue,
 | 
						|
  semaphore
 | 
						|
};
 | 
						|
 | 
						|
static bool
 | 
						|
check_path (char *res_name, ipc_type_t type, const char *name, size_t len)
 | 
						|
{
 | 
						|
  /* Note that we require the existance of the appropriate /dev subdirectories
 | 
						|
     for POSIX IPC object support, similar to Linux (which supports the
 | 
						|
     directories, but doesn't require to mount them).  We don't create
 | 
						|
     these directory here, that's the task of the installer.  But we check
 | 
						|
     for existance and give ample warning. */
 | 
						|
  path_conv path (ipc_names[type].prefix, PC_SYM_NOFOLLOW);
 | 
						|
  if (path.error || !path.exists () || !path.isdir ())
 | 
						|
    {
 | 
						|
      small_printf (
 | 
						|
	"Warning: '%s' does not exists or is not a directory.\n\n"
 | 
						|
	"%ss require the existance of this directory.\n"
 | 
						|
	"Create the directory '%s' and set the permissions to 01777.\n"
 | 
						|
	"For instance on the command line: mkdir -m 01777 %s\n",
 | 
						|
	ipc_names[type].prefix, ipc_names[type].description,
 | 
						|
	ipc_names[type].prefix, ipc_names[type].prefix);
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  /* Name must not be empty, or just be a single slash, or start with more
 | 
						|
     than one slash.  Same for backslash.
 | 
						|
     Apart from handling backslash like slash, the naming rules are identical
 | 
						|
     to Linux, including the names and requirements for subdirectories, if
 | 
						|
     the name contains further slashes. */
 | 
						|
  if (!name || (strchr ("/\\", name[0])
 | 
						|
		&& (!name[1] || strchr ("/\\", name[1]))))
 | 
						|
    {
 | 
						|
      debug_printf ("Invalid %s name '%s'", ipc_names[type].description, name);
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  /* Skip leading (back-)slash. */
 | 
						|
  if (strchr ("/\\", name[0]))
 | 
						|
    ++name;
 | 
						|
  if (len > PATH_MAX - ipc_names[type].prefix_len)
 | 
						|
    {
 | 
						|
      debug_printf ("%s name '%s' too long", ipc_names[type].description, name);
 | 
						|
      set_errno (ENAMETOOLONG);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  __small_sprintf (res_name, "%s/%s%s", ipc_names[type].prefix,
 | 
						|
					type == semaphore ? "sem." : "",
 | 
						|
					name);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
ipc_mutex_init (HANDLE *pmtx, const char *name)
 | 
						|
{
 | 
						|
  WCHAR buf[MAX_PATH];
 | 
						|
  UNICODE_STRING uname;
 | 
						|
  OBJECT_ATTRIBUTES attr;
 | 
						|
  NTSTATUS status;
 | 
						|
 | 
						|
  __small_swprintf (buf, L"mqueue/mtx_%s", name);
 | 
						|
  RtlInitUnicodeString (&uname, buf);
 | 
						|
  InitializeObjectAttributes (&attr, &uname,
 | 
						|
			      OBJ_INHERIT | OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
 | 
						|
			      get_shared_parent_dir (),
 | 
						|
			      everyone_sd (CYG_MUTANT_ACCESS));
 | 
						|
  status = NtCreateMutant (pmtx, CYG_MUTANT_ACCESS, &attr, FALSE);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      debug_printf ("NtCreateMutant: %y", status);
 | 
						|
      return geterrno_from_win_error (RtlNtStatusToDosError (status));
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
ipc_mutex_lock (HANDLE mtx)
 | 
						|
{
 | 
						|
  switch (cygwait (mtx, cw_infinite, cw_sig_eintr | cw_cancel | cw_cancel_self))
 | 
						|
    {
 | 
						|
    case WAIT_OBJECT_0:
 | 
						|
    case WAIT_ABANDONED_0:
 | 
						|
      return 0;
 | 
						|
    case WAIT_SIGNALED:
 | 
						|
      set_errno (EINTR);
 | 
						|
      return 1;
 | 
						|
    default:
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  return geterrno_from_win_error ();
 | 
						|
}
 | 
						|
 | 
						|
static inline int
 | 
						|
ipc_mutex_unlock (HANDLE mtx)
 | 
						|
{
 | 
						|
  return ReleaseMutex (mtx) ? 0 : geterrno_from_win_error ();
 | 
						|
}
 | 
						|
 | 
						|
static inline int
 | 
						|
ipc_mutex_close (HANDLE mtx)
 | 
						|
{
 | 
						|
  return CloseHandle (mtx) ? 0 : geterrno_from_win_error ();
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
ipc_cond_init (HANDLE *pevt, const char *name, char sr)
 | 
						|
{
 | 
						|
  WCHAR buf[MAX_PATH];
 | 
						|
  UNICODE_STRING uname;
 | 
						|
  OBJECT_ATTRIBUTES attr;
 | 
						|
  NTSTATUS status;
 | 
						|
 | 
						|
  __small_swprintf (buf, L"mqueue/evt_%s%c", name, sr);
 | 
						|
  RtlInitUnicodeString (&uname, buf);
 | 
						|
  InitializeObjectAttributes (&attr, &uname,
 | 
						|
			      OBJ_INHERIT | OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
 | 
						|
			      get_shared_parent_dir (),
 | 
						|
			      everyone_sd (CYG_EVENT_ACCESS));
 | 
						|
  status = NtCreateEvent (pevt, CYG_EVENT_ACCESS, &attr,
 | 
						|
			  NotificationEvent, FALSE);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      debug_printf ("NtCreateEvent: %y", status);
 | 
						|
      return geterrno_from_win_error (RtlNtStatusToDosError (status));
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
ipc_cond_timedwait (HANDLE evt, HANDLE mtx, const struct timespec *abstime)
 | 
						|
{
 | 
						|
  HANDLE w4[4] = { evt, };
 | 
						|
  DWORD cnt = 2;
 | 
						|
  DWORD timer_idx = 0;
 | 
						|
  int ret = 0;
 | 
						|
 | 
						|
  set_signal_arrived here (w4[1]);
 | 
						|
  if ((w4[cnt] = pthread::get_cancel_event ()) != NULL)
 | 
						|
    ++cnt;
 | 
						|
  if (abstime)
 | 
						|
    {
 | 
						|
      if (abstime->tv_sec < 0
 | 
						|
	       || abstime->tv_nsec < 0
 | 
						|
	       || abstime->tv_nsec > 999999999)
 | 
						|
	return EINVAL;
 | 
						|
 | 
						|
      /* If a timeout is set, we create a waitable timer to wait for.
 | 
						|
	 This is the easiest way to handle the absolute timeout value, given
 | 
						|
	 that NtSetTimer also takes absolute times and given the double
 | 
						|
	 dependency on evt *and* mtx, which requires to call WFMO twice. */
 | 
						|
      NTSTATUS status;
 | 
						|
      LARGE_INTEGER duetime;
 | 
						|
 | 
						|
      timer_idx = cnt++;
 | 
						|
      status = NtCreateTimer (&w4[timer_idx], TIMER_ALL_ACCESS, NULL,
 | 
						|
			      NotificationTimer);
 | 
						|
      if (!NT_SUCCESS (status))
 | 
						|
	return geterrno_from_nt_status (status);
 | 
						|
      timespec_to_filetime (abstime, &duetime);
 | 
						|
      status = NtSetTimer (w4[timer_idx], &duetime, NULL, NULL, FALSE, 0, NULL);
 | 
						|
      if (!NT_SUCCESS (status))
 | 
						|
	{
 | 
						|
	  NtClose (w4[timer_idx]);
 | 
						|
	  return geterrno_from_nt_status (status);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  ResetEvent (evt);
 | 
						|
  if ((ret = ipc_mutex_unlock (mtx)) != 0)
 | 
						|
    return ret;
 | 
						|
  /* Everything's set up, so now wait for the event to be signalled. */
 | 
						|
restart1:
 | 
						|
  switch (WaitForMultipleObjects (cnt, w4, FALSE, INFINITE))
 | 
						|
    {
 | 
						|
    case WAIT_OBJECT_0:
 | 
						|
      break;
 | 
						|
    case WAIT_OBJECT_0 + 1:
 | 
						|
      if (_my_tls.call_signal_handler ())
 | 
						|
	goto restart1;
 | 
						|
      ret = EINTR;
 | 
						|
      break;
 | 
						|
    case WAIT_OBJECT_0 + 2:
 | 
						|
      if (timer_idx != 2)
 | 
						|
	pthread::static_cancel_self ();
 | 
						|
      /*FALLTHRU*/
 | 
						|
    case WAIT_OBJECT_0 + 3:
 | 
						|
      ret = ETIMEDOUT;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      ret = geterrno_from_win_error ();
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  if (ret == 0)
 | 
						|
    {
 | 
						|
      /* At this point we need to lock the mutex.  The wait is practically
 | 
						|
	 the same as before, just that we now wait on the mutex instead of the
 | 
						|
	 event. */
 | 
						|
    restart2:
 | 
						|
      w4[0] = mtx;
 | 
						|
      switch (WaitForMultipleObjects (cnt, w4, FALSE, INFINITE))
 | 
						|
	{
 | 
						|
	case WAIT_OBJECT_0:
 | 
						|
	case WAIT_ABANDONED_0:
 | 
						|
	  break;
 | 
						|
	case WAIT_OBJECT_0 + 1:
 | 
						|
	  if (_my_tls.call_signal_handler ())
 | 
						|
	    goto restart2;
 | 
						|
	  ret = EINTR;
 | 
						|
	  break;
 | 
						|
	case WAIT_OBJECT_0 + 2:
 | 
						|
	  if (timer_idx != 2)
 | 
						|
	    pthread_testcancel ();
 | 
						|
	  /*FALLTHRU*/
 | 
						|
	case WAIT_OBJECT_0 + 3:
 | 
						|
	  ret = ETIMEDOUT;
 | 
						|
	  break;
 | 
						|
	default:
 | 
						|
	  ret = geterrno_from_win_error ();
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  if (timer_idx)
 | 
						|
    {
 | 
						|
      if (ret != ETIMEDOUT)
 | 
						|
	NtCancelTimer (w4[timer_idx], NULL);
 | 
						|
      NtClose (w4[timer_idx]);
 | 
						|
    }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
static inline void
 | 
						|
ipc_cond_signal (HANDLE evt)
 | 
						|
{
 | 
						|
  SetEvent (evt);
 | 
						|
}
 | 
						|
 | 
						|
static inline void
 | 
						|
ipc_cond_close (HANDLE evt)
 | 
						|
{
 | 
						|
  CloseHandle (evt);
 | 
						|
}
 | 
						|
 | 
						|
class ipc_flock
 | 
						|
{
 | 
						|
  struct flock fl;
 | 
						|
 | 
						|
public:
 | 
						|
  ipc_flock () { memset (&fl, 0, sizeof fl); }
 | 
						|
 | 
						|
  int lock (int fd, size_t size)
 | 
						|
  {
 | 
						|
    fl.l_type = F_WRLCK;
 | 
						|
    fl.l_whence = SEEK_SET;
 | 
						|
    fl.l_start = 0;
 | 
						|
    fl.l_len = size;
 | 
						|
    return fcntl64 (fd, F_SETLKW, &fl);
 | 
						|
  }
 | 
						|
  int unlock (int fd)
 | 
						|
  {
 | 
						|
    if (!fl.l_len)
 | 
						|
      return 0;
 | 
						|
    fl.l_type = F_UNLCK;
 | 
						|
    return fcntl64 (fd, F_SETLKW, &fl);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
/* POSIX shared memory object implementation. */
 | 
						|
 | 
						|
extern "C" int
 | 
						|
shm_open (const char *name, int oflag, mode_t mode)
 | 
						|
{
 | 
						|
  size_t len = strlen (name);
 | 
						|
  char shmname[ipc_names[shmem].prefix_len + len];
 | 
						|
 | 
						|
  if (!check_path (shmname, shmem, name, len))
 | 
						|
    return -1;
 | 
						|
 | 
						|
  /* Check for valid flags. */
 | 
						|
  if (((oflag & O_ACCMODE) != O_RDONLY && (oflag & O_ACCMODE) != O_RDWR)
 | 
						|
      || (oflag & ~(O_ACCMODE | O_CREAT | O_EXCL | O_TRUNC)))
 | 
						|
    {
 | 
						|
      debug_printf ("Invalid oflag 0%o", oflag);
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  return open (shmname, oflag | O_CLOEXEC, mode & 0777);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
shm_unlink (const char *name)
 | 
						|
{
 | 
						|
  size_t len = strlen (name);
 | 
						|
  char shmname[ipc_names[shmem].prefix_len + len];
 | 
						|
 | 
						|
  if (!check_path (shmname, shmem, name, len))
 | 
						|
    return -1;
 | 
						|
 | 
						|
  return unlink (shmname);
 | 
						|
}
 | 
						|
 | 
						|
/* The POSIX message queue implementation is based on W. Richard STEVENS
 | 
						|
   implementation, just tweaked for Cygwin.  The main change is
 | 
						|
   the usage of Windows mutexes and events instead of using the pthread
 | 
						|
   synchronization objects.  The pathname is massaged so that the
 | 
						|
   files are created under /dev/mqueue.  mq_timedsend and mq_timedreceive
 | 
						|
   are implemented additionally. */
 | 
						|
 | 
						|
/* The mq_attr structure is defined using long datatypes per POSIX.
 | 
						|
   For interoperability reasons between 32 and 64 bit processes, we have
 | 
						|
   to make sure to use a unified structure layout in the message queue file.
 | 
						|
   That's what the mq_fattr is, the in-file representation of the mq_attr
 | 
						|
   struct. */
 | 
						|
#pragma pack (push, 4)
 | 
						|
struct mq_fattr
 | 
						|
{
 | 
						|
  uint32_t mq_flags;
 | 
						|
  uint32_t mq_maxmsg;
 | 
						|
  uint32_t mq_msgsize;
 | 
						|
  uint32_t mq_curmsgs;
 | 
						|
};
 | 
						|
 | 
						|
struct mq_hdr
 | 
						|
{
 | 
						|
  struct mq_fattr mqh_attr;	 /* the queue's attributes */
 | 
						|
  int32_t         mqh_head;	 /* index of first message */
 | 
						|
  int32_t         mqh_free;	 /* index of first free message */
 | 
						|
  int32_t         mqh_nwait;	 /* #threads blocked in mq_receive() */
 | 
						|
  pid_t           mqh_pid;	 /* nonzero PID if mqh_event set */
 | 
						|
  char            mqh_uname[36]; /* unique name used to identify synchronization
 | 
						|
				    objects connected to this queue */
 | 
						|
  union {
 | 
						|
    struct sigevent mqh_event;	 /* for mq_notify() */
 | 
						|
    /* Make sure sigevent takes the same space on 32 and 64 bit systems.
 | 
						|
       Other than that, it doesn't need to be compatible since only
 | 
						|
       one process can be notified at a time. */
 | 
						|
    uint64_t        mqh_placeholder[8];
 | 
						|
  };
 | 
						|
  uint32_t        mqh_magic;	/* Expect MQI_MAGIC here, otherwise it's
 | 
						|
				   an old-style message queue. */
 | 
						|
};
 | 
						|
 | 
						|
struct msg_hdr
 | 
						|
{
 | 
						|
  int32_t         msg_next;	 /* index of next on linked list */
 | 
						|
  int32_t         msg_len;	 /* actual length */
 | 
						|
  unsigned int    msg_prio;	 /* priority */
 | 
						|
};
 | 
						|
#pragma pack (pop)
 | 
						|
 | 
						|
struct mq_info
 | 
						|
{
 | 
						|
  struct mq_hdr  *mqi_hdr;	 /* start of mmap'ed region */
 | 
						|
  uint32_t        mqi_magic;	 /* magic number if open */
 | 
						|
  int             mqi_flags;	 /* flags for this process */
 | 
						|
  HANDLE          mqi_lock;	 /* mutex lock */
 | 
						|
  HANDLE          mqi_waitsend;	 /* and condition variable for full queue */
 | 
						|
  HANDLE          mqi_waitrecv;	 /* and condition variable for empty queue */
 | 
						|
};
 | 
						|
 | 
						|
#define MQI_MAGIC	0x98765432UL
 | 
						|
 | 
						|
#define MSGSIZE(i)	roundup((i), sizeof(long))
 | 
						|
 | 
						|
#define	 MAX_TRIES	10	/* for waiting for initialization */
 | 
						|
 | 
						|
struct mq_attr defattr = { 0, 10, 8192, 0 };	/* Linux defaults. */
 | 
						|
 | 
						|
extern "C" off_t lseek64 (int, off_t, int);
 | 
						|
extern "C" void *mmap64 (void *, size_t, int, int, int, off_t);
 | 
						|
 | 
						|
extern "C" mqd_t
 | 
						|
mq_open (const char *name, int oflag, ...)
 | 
						|
{
 | 
						|
  int i, fd = -1, nonblock, created;
 | 
						|
  long msgsize, index;
 | 
						|
  off_t filesize = 0;
 | 
						|
  va_list ap;
 | 
						|
  mode_t mode;
 | 
						|
  int8_t *mptr;
 | 
						|
  struct stat statbuff;
 | 
						|
  struct mq_hdr *mqhdr;
 | 
						|
  struct msg_hdr *msghdr;
 | 
						|
  struct mq_attr *attr;
 | 
						|
  struct mq_info *mqinfo;
 | 
						|
  LUID luid;
 | 
						|
 | 
						|
  size_t len = strlen (name);
 | 
						|
  char mqname[ipc_names[mqueue].prefix_len + len];
 | 
						|
 | 
						|
  if (!check_path (mqname, mqueue, name, len))
 | 
						|
    return (mqd_t) -1;
 | 
						|
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EFAULT))
 | 
						|
    return (mqd_t) -1;
 | 
						|
 | 
						|
  oflag &= (O_CREAT | O_EXCL | O_NONBLOCK);
 | 
						|
  created = 0;
 | 
						|
  nonblock = oflag & O_NONBLOCK;
 | 
						|
  oflag &= ~O_NONBLOCK;
 | 
						|
  mptr = (int8_t *) MAP_FAILED;
 | 
						|
  mqinfo = NULL;
 | 
						|
 | 
						|
again:
 | 
						|
  if (oflag & O_CREAT)
 | 
						|
    {
 | 
						|
      va_start (ap, oflag);		/* init ap to final named argument */
 | 
						|
      mode = va_arg (ap, mode_t) & ~S_IXUSR;
 | 
						|
      attr = va_arg (ap, struct mq_attr *);
 | 
						|
      va_end (ap);
 | 
						|
 | 
						|
      /* Open and specify O_EXCL and user-execute */
 | 
						|
      fd = open (mqname, oflag | O_EXCL | O_RDWR | O_CLOEXEC, mode | S_IXUSR);
 | 
						|
      if (fd < 0)
 | 
						|
	{
 | 
						|
	  if (errno == EEXIST && (oflag & O_EXCL) == 0)
 | 
						|
	    goto exists;		/* already exists, OK */
 | 
						|
	  return (mqd_t) -1;
 | 
						|
	}
 | 
						|
      created = 1;
 | 
						|
      /* First one to create the file initializes it */
 | 
						|
      if (attr == NULL)
 | 
						|
	attr = &defattr;
 | 
						|
      /* Check minimum and maximum values.  The max values are pretty much
 | 
						|
         arbitrary, taken from the linux mq_overview man page.  However,
 | 
						|
	 these max values make sure that the internal mq_fattr structure
 | 
						|
	 can use 32 bit types. */
 | 
						|
      else if (attr->mq_maxmsg <= 0 || attr->mq_maxmsg > 32768
 | 
						|
	       || attr->mq_msgsize <= 0 || attr->mq_msgsize > 1048576)
 | 
						|
	{
 | 
						|
	  set_errno (EINVAL);
 | 
						|
	  goto err;
 | 
						|
	}
 | 
						|
      /* Calculate and set the file size */
 | 
						|
      msgsize = MSGSIZE (attr->mq_msgsize);
 | 
						|
      filesize = sizeof (struct mq_hdr)
 | 
						|
		 + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
 | 
						|
      if (lseek64 (fd, filesize - 1, SEEK_SET) == -1)
 | 
						|
	goto err;
 | 
						|
      if (write (fd, "", 1) == -1)
 | 
						|
	goto err;
 | 
						|
 | 
						|
      /* Memory map the file */
 | 
						|
      mptr = (int8_t *) mmap64 (NULL, (size_t) filesize, PROT_READ | PROT_WRITE,
 | 
						|
				MAP_SHARED, fd, 0);
 | 
						|
      if (mptr == (int8_t *) MAP_FAILED)
 | 
						|
	goto err;
 | 
						|
 | 
						|
      /* Allocate one mq_info{} for the queue */
 | 
						|
      if (!(mqinfo = (struct mq_info *) calloc (1, sizeof (struct mq_info))))
 | 
						|
	goto err;
 | 
						|
      mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr;
 | 
						|
      mqinfo->mqi_magic = MQI_MAGIC;
 | 
						|
      mqinfo->mqi_flags = nonblock;
 | 
						|
 | 
						|
      /* Initialize header at beginning of file */
 | 
						|
      /* Create free list with all messages on it */
 | 
						|
      mqhdr->mqh_attr.mq_flags = 0;
 | 
						|
      mqhdr->mqh_attr.mq_maxmsg = attr->mq_maxmsg;
 | 
						|
      mqhdr->mqh_attr.mq_msgsize = attr->mq_msgsize;
 | 
						|
      mqhdr->mqh_attr.mq_curmsgs = 0;
 | 
						|
      mqhdr->mqh_nwait = 0;
 | 
						|
      mqhdr->mqh_pid = 0;
 | 
						|
      NtAllocateLocallyUniqueId (&luid);
 | 
						|
      __small_sprintf (mqhdr->mqh_uname, "%016X%08x%08x",
 | 
						|
		       hash_path_name (0,mqname),
 | 
						|
		       luid.HighPart, luid.LowPart);
 | 
						|
      mqhdr->mqh_head = 0;
 | 
						|
      mqhdr->mqh_magic = MQI_MAGIC;
 | 
						|
      index = sizeof (struct mq_hdr);
 | 
						|
      mqhdr->mqh_free = index;
 | 
						|
      for (i = 0; i < attr->mq_maxmsg - 1; i++)
 | 
						|
	{
 | 
						|
	  msghdr = (struct msg_hdr *) &mptr[index];
 | 
						|
	  index += sizeof (struct msg_hdr) + msgsize;
 | 
						|
	  msghdr->msg_next = index;
 | 
						|
	}
 | 
						|
      msghdr = (struct msg_hdr *) &mptr[index];
 | 
						|
      msghdr->msg_next = 0;		/* end of free list */
 | 
						|
 | 
						|
      /* Initialize mutex & condition variables */
 | 
						|
      i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname);
 | 
						|
      if (i != 0)
 | 
						|
	goto pthreaderr;
 | 
						|
 | 
						|
      i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S');
 | 
						|
      if (i != 0)
 | 
						|
	goto pthreaderr;
 | 
						|
 | 
						|
      i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R');
 | 
						|
      if (i != 0)
 | 
						|
	goto pthreaderr;
 | 
						|
 | 
						|
      /* Initialization complete, turn off user-execute bit */
 | 
						|
      if (fchmod (fd, mode) == -1)
 | 
						|
	goto err;
 | 
						|
      close (fd);
 | 
						|
      return ((mqd_t) mqinfo);
 | 
						|
    }
 | 
						|
 | 
						|
exists:
 | 
						|
  /* Open the file then memory map */
 | 
						|
  if ((fd = open (mqname, O_RDWR | O_CLOEXEC)) < 0)
 | 
						|
    {
 | 
						|
      if (errno == ENOENT && (oflag & O_CREAT))
 | 
						|
	goto again;
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  /* Make certain initialization is complete */
 | 
						|
  for (i = 0; i < MAX_TRIES; i++)
 | 
						|
    {
 | 
						|
      if (stat64 (mqname, &statbuff) == -1)
 | 
						|
	{
 | 
						|
	  if (errno == ENOENT && (oflag & O_CREAT))
 | 
						|
	    {
 | 
						|
	      close (fd);
 | 
						|
	      fd = -1;
 | 
						|
	      goto again;
 | 
						|
	    }
 | 
						|
	  goto err;
 | 
						|
	}
 | 
						|
      if ((statbuff.st_mode & S_IXUSR) == 0)
 | 
						|
	break;
 | 
						|
      sleep (1);
 | 
						|
    }
 | 
						|
  if (i == MAX_TRIES)
 | 
						|
    {
 | 
						|
      set_errno (ETIMEDOUT);
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
 | 
						|
  filesize = statbuff.st_size;
 | 
						|
  mptr = (int8_t *) mmap64 (NULL, (size_t) filesize, PROT_READ | PROT_WRITE,
 | 
						|
			    MAP_SHARED, fd, 0);
 | 
						|
  if (mptr == (int8_t *) MAP_FAILED)
 | 
						|
    goto err;
 | 
						|
  close (fd);
 | 
						|
  fd = -1;
 | 
						|
 | 
						|
  /* Allocate one mq_info{} for each open */
 | 
						|
  if (!(mqinfo = (struct mq_info *) calloc (1, sizeof (struct mq_info))))
 | 
						|
    goto err;
 | 
						|
  mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr;
 | 
						|
  if (mqhdr->mqh_magic != MQI_MAGIC)
 | 
						|
    {
 | 
						|
      system_printf (
 | 
						|
"Old message queue \"%s\" detected!\n"
 | 
						|
"This file is not usable as message queue anymore due to changes in the "
 | 
						|
"internal file layout.  Please remove the file and try again.", mqname);
 | 
						|
      set_errno (EACCES);
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  mqinfo->mqi_magic = MQI_MAGIC;
 | 
						|
  mqinfo->mqi_flags = nonblock;
 | 
						|
 | 
						|
  /* Initialize mutex & condition variable */
 | 
						|
  i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname);
 | 
						|
  if (i != 0)
 | 
						|
    goto pthreaderr;
 | 
						|
 | 
						|
  i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S');
 | 
						|
  if (i != 0)
 | 
						|
    goto pthreaderr;
 | 
						|
 | 
						|
  i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R');
 | 
						|
  if (i != 0)
 | 
						|
    goto pthreaderr;
 | 
						|
 | 
						|
  return (mqd_t) mqinfo;
 | 
						|
 | 
						|
pthreaderr:
 | 
						|
  errno = i;
 | 
						|
err:
 | 
						|
  /* Don't let following function calls change errno */
 | 
						|
  save_errno save;
 | 
						|
 | 
						|
  if (created)
 | 
						|
    unlink (mqname);
 | 
						|
  if (mptr != (int8_t *) MAP_FAILED)
 | 
						|
    munmap((void *) mptr, (size_t) filesize);
 | 
						|
  if (mqinfo)
 | 
						|
    {
 | 
						|
      if (mqinfo->mqi_lock)
 | 
						|
	ipc_mutex_close (mqinfo->mqi_lock);
 | 
						|
      if (mqinfo->mqi_waitsend)
 | 
						|
	ipc_cond_close (mqinfo->mqi_waitsend);
 | 
						|
      if (mqinfo->mqi_waitrecv)
 | 
						|
	ipc_cond_close (mqinfo->mqi_waitrecv);
 | 
						|
      free (mqinfo);
 | 
						|
    }
 | 
						|
  if (fd >= 0)
 | 
						|
    close (fd);
 | 
						|
  return (mqd_t) -1;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
mq_getattr (mqd_t mqd, struct mq_attr *mqstat)
 | 
						|
{
 | 
						|
  int n;
 | 
						|
  struct mq_hdr *mqhdr;
 | 
						|
  struct mq_fattr *attr;
 | 
						|
  struct mq_info *mqinfo;
 | 
						|
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EBADF))
 | 
						|
      return -1;
 | 
						|
 | 
						|
  mqinfo = (struct mq_info *) mqd;
 | 
						|
  if (mqinfo->mqi_magic != MQI_MAGIC)
 | 
						|
    {
 | 
						|
      set_errno (EBADF);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  mqhdr = mqinfo->mqi_hdr;
 | 
						|
  attr = &mqhdr->mqh_attr;
 | 
						|
  if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
 | 
						|
    {
 | 
						|
      errno = n;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  mqstat->mq_flags = mqinfo->mqi_flags;   /* per-open */
 | 
						|
  mqstat->mq_maxmsg = attr->mq_maxmsg;    /* remaining three per-queue */
 | 
						|
  mqstat->mq_msgsize = attr->mq_msgsize;
 | 
						|
  mqstat->mq_curmsgs = attr->mq_curmsgs;
 | 
						|
 | 
						|
  ipc_mutex_unlock (mqinfo->mqi_lock);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
mq_setattr (mqd_t mqd, const struct mq_attr *mqstat, struct mq_attr *omqstat)
 | 
						|
{
 | 
						|
  int n;
 | 
						|
  struct mq_hdr *mqhdr;
 | 
						|
  struct mq_fattr *attr;
 | 
						|
  struct mq_info *mqinfo;
 | 
						|
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EBADF))
 | 
						|
      return -1;
 | 
						|
 | 
						|
  mqinfo = (struct mq_info *) mqd;
 | 
						|
  if (mqinfo->mqi_magic != MQI_MAGIC)
 | 
						|
    {
 | 
						|
      set_errno (EBADF);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  mqhdr = mqinfo->mqi_hdr;
 | 
						|
  attr = &mqhdr->mqh_attr;
 | 
						|
  if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
 | 
						|
    {
 | 
						|
      errno = n;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  if (omqstat != NULL)
 | 
						|
    {
 | 
						|
      omqstat->mq_flags = mqinfo->mqi_flags;  /* previous attributes */
 | 
						|
      omqstat->mq_maxmsg = attr->mq_maxmsg;
 | 
						|
      omqstat->mq_msgsize = attr->mq_msgsize;
 | 
						|
      omqstat->mq_curmsgs = attr->mq_curmsgs; /* and current status */
 | 
						|
    }
 | 
						|
 | 
						|
  if (mqstat->mq_flags & O_NONBLOCK)
 | 
						|
    mqinfo->mqi_flags |= O_NONBLOCK;
 | 
						|
  else
 | 
						|
    mqinfo->mqi_flags &= ~O_NONBLOCK;
 | 
						|
 | 
						|
  ipc_mutex_unlock (mqinfo->mqi_lock);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
mq_notify (mqd_t mqd, const struct sigevent *notification)
 | 
						|
{
 | 
						|
  int n;
 | 
						|
  pid_t pid;
 | 
						|
  struct mq_hdr *mqhdr;
 | 
						|
  struct mq_info *mqinfo;
 | 
						|
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EBADF))
 | 
						|
      return -1;
 | 
						|
 | 
						|
  mqinfo = (struct mq_info *) mqd;
 | 
						|
  if (mqinfo->mqi_magic != MQI_MAGIC)
 | 
						|
    {
 | 
						|
      set_errno (EBADF);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  mqhdr = mqinfo->mqi_hdr;
 | 
						|
  if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
 | 
						|
    {
 | 
						|
      errno = n;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  pid = getpid ();
 | 
						|
  if (!notification)
 | 
						|
    {
 | 
						|
      if (mqhdr->mqh_pid == pid)
 | 
						|
	  mqhdr->mqh_pid = 0;     /* unregister calling process */
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (mqhdr->mqh_pid != 0)
 | 
						|
	{
 | 
						|
	  if (kill (mqhdr->mqh_pid, 0) != -1 || errno != ESRCH)
 | 
						|
	    {
 | 
						|
	      set_errno (EBUSY);
 | 
						|
	      ipc_mutex_unlock (mqinfo->mqi_lock);
 | 
						|
	      return -1;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      mqhdr->mqh_pid = pid;
 | 
						|
      mqhdr->mqh_event = *notification;
 | 
						|
    }
 | 
						|
  ipc_mutex_unlock (mqinfo->mqi_lock);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
_mq_send (mqd_t mqd, const char *ptr, size_t len, unsigned int prio,
 | 
						|
	  const struct timespec *abstime)
 | 
						|
{
 | 
						|
  int n;
 | 
						|
  long index, freeindex;
 | 
						|
  int8_t *mptr;
 | 
						|
  struct sigevent *sigev;
 | 
						|
  struct mq_hdr *mqhdr;
 | 
						|
  struct mq_fattr *attr;
 | 
						|
  struct msg_hdr *msghdr, *nmsghdr, *pmsghdr;
 | 
						|
  struct mq_info *mqinfo;
 | 
						|
 | 
						|
  pthread_testcancel ();
 | 
						|
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EBADF))
 | 
						|
      return -1;
 | 
						|
 | 
						|
  mqinfo = (struct mq_info *) mqd;
 | 
						|
  if (mqinfo->mqi_magic != MQI_MAGIC)
 | 
						|
    {
 | 
						|
      set_errno (EBADF);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  if (prio > MQ_PRIO_MAX)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  mqhdr = mqinfo->mqi_hdr;        /* struct pointer */
 | 
						|
  mptr = (int8_t *) mqhdr;        /* byte pointer */
 | 
						|
  attr = &mqhdr->mqh_attr;
 | 
						|
  if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
 | 
						|
    {
 | 
						|
      errno = n;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  if (len > (size_t) attr->mq_msgsize)
 | 
						|
    {
 | 
						|
      set_errno (EMSGSIZE);
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  if (attr->mq_curmsgs == 0)
 | 
						|
    {
 | 
						|
      if (mqhdr->mqh_pid != 0 && mqhdr->mqh_nwait == 0)
 | 
						|
	{
 | 
						|
	  sigev = &mqhdr->mqh_event;
 | 
						|
	  if (sigev->sigev_notify == SIGEV_SIGNAL)
 | 
						|
	    sigqueue (mqhdr->mqh_pid, sigev->sigev_signo, sigev->sigev_value);
 | 
						|
	  mqhdr->mqh_pid = 0;             /* unregister */
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else if (attr->mq_curmsgs >= attr->mq_maxmsg)
 | 
						|
    {
 | 
						|
      /* Queue is full */
 | 
						|
      if (mqinfo->mqi_flags & O_NONBLOCK)
 | 
						|
	{
 | 
						|
	  set_errno (EAGAIN);
 | 
						|
	  goto err;
 | 
						|
	}
 | 
						|
      /* Wait for room for one message on the queue */
 | 
						|
      while (attr->mq_curmsgs >= attr->mq_maxmsg)
 | 
						|
	{
 | 
						|
	  int ret = ipc_cond_timedwait (mqinfo->mqi_waitsend, mqinfo->mqi_lock,
 | 
						|
					abstime);
 | 
						|
	  if (ret != 0)
 | 
						|
	    {
 | 
						|
	      set_errno (ret);
 | 
						|
	      return -1;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* nmsghdr will point to new message */
 | 
						|
  if ((freeindex = mqhdr->mqh_free) == 0)
 | 
						|
    api_fatal ("mq_send: curmsgs = %ld; free = 0", attr->mq_curmsgs);
 | 
						|
 | 
						|
  nmsghdr = (struct msg_hdr *) &mptr[freeindex];
 | 
						|
  nmsghdr->msg_prio = prio;
 | 
						|
  nmsghdr->msg_len = len;
 | 
						|
  memcpy (nmsghdr + 1, ptr, len);          /* copy message from caller */
 | 
						|
  mqhdr->mqh_free = nmsghdr->msg_next;    /* new freelist head */
 | 
						|
 | 
						|
  /* Find right place for message in linked list */
 | 
						|
  index = mqhdr->mqh_head;
 | 
						|
  pmsghdr = (struct msg_hdr *) &(mqhdr->mqh_head);
 | 
						|
  while (index)
 | 
						|
    {
 | 
						|
      msghdr = (struct msg_hdr *) &mptr[index];
 | 
						|
      if (prio > msghdr->msg_prio)
 | 
						|
	{
 | 
						|
	  nmsghdr->msg_next = index;
 | 
						|
	  pmsghdr->msg_next = freeindex;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      index = msghdr->msg_next;
 | 
						|
      pmsghdr = msghdr;
 | 
						|
    }
 | 
						|
  if (index == 0)
 | 
						|
    {
 | 
						|
      /* Queue was empty or new goes at end of list */
 | 
						|
      pmsghdr->msg_next = freeindex;
 | 
						|
      nmsghdr->msg_next = 0;
 | 
						|
    }
 | 
						|
  /* Wake up anyone blocked in mq_receive waiting for a message */
 | 
						|
  if (attr->mq_curmsgs == 0)
 | 
						|
    ipc_cond_signal (mqinfo->mqi_waitrecv);
 | 
						|
  attr->mq_curmsgs++;
 | 
						|
 | 
						|
  ipc_mutex_unlock (mqinfo->mqi_lock);
 | 
						|
  return 0;
 | 
						|
 | 
						|
err:
 | 
						|
  ipc_mutex_unlock (mqinfo->mqi_lock);
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
mq_send (mqd_t mqd, const char *ptr, size_t len, unsigned int prio)
 | 
						|
{
 | 
						|
  return _mq_send (mqd, ptr, len, prio, NULL);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
mq_timedsend (mqd_t mqd, const char *ptr, size_t len, unsigned int prio,
 | 
						|
	      const struct timespec *abstime)
 | 
						|
{
 | 
						|
  return _mq_send (mqd, ptr, len, prio, abstime);
 | 
						|
}
 | 
						|
 | 
						|
static ssize_t
 | 
						|
_mq_receive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop,
 | 
						|
	     const struct timespec *abstime)
 | 
						|
{
 | 
						|
  int n;
 | 
						|
  long index;
 | 
						|
  int8_t *mptr;
 | 
						|
  ssize_t len;
 | 
						|
  struct mq_hdr *mqhdr;
 | 
						|
  struct mq_fattr *attr;
 | 
						|
  struct msg_hdr *msghdr;
 | 
						|
  struct mq_info *mqinfo;
 | 
						|
 | 
						|
  pthread_testcancel ();
 | 
						|
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EBADF))
 | 
						|
      return -1;
 | 
						|
 | 
						|
  mqinfo = (struct mq_info *) mqd;
 | 
						|
  if (mqinfo->mqi_magic != MQI_MAGIC)
 | 
						|
    {
 | 
						|
      set_errno (EBADF);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  mqhdr = mqinfo->mqi_hdr;        /* struct pointer */
 | 
						|
  mptr = (int8_t *) mqhdr;        /* byte pointer */
 | 
						|
  attr = &mqhdr->mqh_attr;
 | 
						|
  if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
 | 
						|
    {
 | 
						|
      errno = n;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  if (maxlen < (size_t) attr->mq_msgsize)
 | 
						|
    {
 | 
						|
      set_errno (EMSGSIZE);
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  if (attr->mq_curmsgs == 0)	/* queue is empty */
 | 
						|
    {
 | 
						|
      if (mqinfo->mqi_flags & O_NONBLOCK)
 | 
						|
	{
 | 
						|
	  set_errno (EAGAIN);
 | 
						|
	  goto err;
 | 
						|
	}
 | 
						|
      /* Wait for a message to be placed onto queue */
 | 
						|
      mqhdr->mqh_nwait++;
 | 
						|
      while (attr->mq_curmsgs == 0)
 | 
						|
	{
 | 
						|
	  int ret = ipc_cond_timedwait (mqinfo->mqi_waitrecv, mqinfo->mqi_lock,
 | 
						|
					abstime);
 | 
						|
	  if (ret != 0)
 | 
						|
	    {
 | 
						|
	      set_errno (ret);
 | 
						|
	      return -1;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      mqhdr->mqh_nwait--;
 | 
						|
    }
 | 
						|
 | 
						|
  if ((index = mqhdr->mqh_head) == 0)
 | 
						|
    api_fatal ("mq_receive: curmsgs = %ld; head = 0", attr->mq_curmsgs);
 | 
						|
 | 
						|
  msghdr = (struct msg_hdr *) &mptr[index];
 | 
						|
  mqhdr->mqh_head = msghdr->msg_next;     /* new head of list */
 | 
						|
  len = msghdr->msg_len;
 | 
						|
  memcpy(ptr, msghdr + 1, len);           /* copy the message itself */
 | 
						|
  if (priop != NULL)
 | 
						|
    *priop = msghdr->msg_prio;
 | 
						|
 | 
						|
  /* Just-read message goes to front of free list */
 | 
						|
  msghdr->msg_next = mqhdr->mqh_free;
 | 
						|
  mqhdr->mqh_free = index;
 | 
						|
 | 
						|
  /* Wake up anyone blocked in mq_send waiting for room */
 | 
						|
  if (attr->mq_curmsgs == attr->mq_maxmsg)
 | 
						|
    ipc_cond_signal (mqinfo->mqi_waitsend);
 | 
						|
  attr->mq_curmsgs--;
 | 
						|
 | 
						|
  ipc_mutex_unlock (mqinfo->mqi_lock);
 | 
						|
  return len;
 | 
						|
 | 
						|
err:
 | 
						|
  ipc_mutex_unlock (mqinfo->mqi_lock);
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" ssize_t
 | 
						|
mq_receive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop)
 | 
						|
{
 | 
						|
  return _mq_receive (mqd, ptr, maxlen, priop, NULL);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" ssize_t
 | 
						|
mq_timedreceive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop,
 | 
						|
		 const struct timespec *abstime)
 | 
						|
{
 | 
						|
  return _mq_receive (mqd, ptr, maxlen, priop, abstime);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
mq_close (mqd_t mqd)
 | 
						|
{
 | 
						|
  long msgsize, filesize;
 | 
						|
  struct mq_hdr *mqhdr;
 | 
						|
  struct mq_fattr *attr;
 | 
						|
  struct mq_info *mqinfo;
 | 
						|
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EBADF))
 | 
						|
      return -1;
 | 
						|
 | 
						|
  mqinfo = (struct mq_info *) mqd;
 | 
						|
  if (mqinfo->mqi_magic != MQI_MAGIC)
 | 
						|
    {
 | 
						|
      set_errno (EBADF);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  mqhdr = mqinfo->mqi_hdr;
 | 
						|
  attr = &mqhdr->mqh_attr;
 | 
						|
 | 
						|
  if (mq_notify (mqd, NULL))	/* unregister calling process */
 | 
						|
    return -1;
 | 
						|
 | 
						|
  msgsize = MSGSIZE (attr->mq_msgsize);
 | 
						|
  filesize = sizeof (struct mq_hdr)
 | 
						|
	     + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
 | 
						|
  if (munmap (mqinfo->mqi_hdr, filesize) == -1)
 | 
						|
    return -1;
 | 
						|
 | 
						|
  mqinfo->mqi_magic = 0;          /* just in case */
 | 
						|
  ipc_cond_close (mqinfo->mqi_waitsend);
 | 
						|
  ipc_cond_close (mqinfo->mqi_waitrecv);
 | 
						|
  ipc_mutex_close (mqinfo->mqi_lock);
 | 
						|
  free (mqinfo);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
mq_unlink (const char *name)
 | 
						|
{
 | 
						|
  size_t len = strlen (name);
 | 
						|
  char mqname[ipc_names[mqueue].prefix_len + len];
 | 
						|
 | 
						|
  if (!check_path (mqname, mqueue, name, len))
 | 
						|
    return -1;
 | 
						|
  if (unlink (mqname) == -1)
 | 
						|
    return -1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* POSIX named semaphore implementation.  Loosely based on Richard W. STEPHENS
 | 
						|
   implementation as far as sem_open is concerned, but under the hood using
 | 
						|
   the already existing semaphore class in thread.cc.  Using a file backed
 | 
						|
   solution allows to implement kernel persistent named semaphores.  */
 | 
						|
 | 
						|
struct sem_finfo
 | 
						|
{
 | 
						|
  unsigned int       value;
 | 
						|
  unsigned long long hash;
 | 
						|
  LUID               luid;
 | 
						|
};
 | 
						|
 | 
						|
extern "C" sem_t *
 | 
						|
sem_open (const char *name, int oflag, ...)
 | 
						|
{
 | 
						|
  int i, fd = -1, created;
 | 
						|
  va_list ap;
 | 
						|
  mode_t mode = 0;
 | 
						|
  unsigned int value = 0;
 | 
						|
  struct stat statbuff;
 | 
						|
  sem_t *sem = SEM_FAILED;
 | 
						|
  sem_finfo sf;
 | 
						|
  bool wasopen = false;
 | 
						|
  ipc_flock file;
 | 
						|
 | 
						|
  size_t len = strlen (name);
 | 
						|
  char semname[ipc_names[semaphore].prefix_len + len];
 | 
						|
 | 
						|
  if (!check_path (semname, semaphore, name, len))
 | 
						|
    return SEM_FAILED;
 | 
						|
 | 
						|
  myfault efault;
 | 
						|
  if (efault.faulted (EFAULT))
 | 
						|
    return SEM_FAILED;
 | 
						|
 | 
						|
  created = 0;
 | 
						|
  oflag &= (O_CREAT | O_EXCL);
 | 
						|
 | 
						|
again:
 | 
						|
  if (oflag & O_CREAT)
 | 
						|
    {
 | 
						|
      va_start (ap, oflag);		/* init ap to final named argument */
 | 
						|
      mode = va_arg (ap, mode_t) & ~S_IXUSR;
 | 
						|
      value = va_arg (ap, unsigned int);
 | 
						|
      va_end (ap);
 | 
						|
 | 
						|
      /* Open and specify O_EXCL and user-execute */
 | 
						|
      fd = open (semname, oflag | O_EXCL | O_RDWR | O_CLOEXEC, mode | S_IXUSR);
 | 
						|
      if (fd < 0)
 | 
						|
	{
 | 
						|
	  if (errno == EEXIST && (oflag & O_EXCL) == 0)
 | 
						|
	    goto exists;		/* already exists, OK */
 | 
						|
	  return SEM_FAILED;
 | 
						|
	}
 | 
						|
      created = 1;
 | 
						|
      /* First one to create the file initializes it. */
 | 
						|
      NtAllocateLocallyUniqueId (&sf.luid);
 | 
						|
      sf.value = value;
 | 
						|
      sf.hash = hash_path_name (0, semname);
 | 
						|
      if (write (fd, &sf, sizeof sf) != sizeof sf)
 | 
						|
	goto err;
 | 
						|
      sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, value, wasopen);
 | 
						|
      if (sem == SEM_FAILED)
 | 
						|
	goto err;
 | 
						|
      /* Initialization complete, turn off user-execute bit */
 | 
						|
      if (fchmod (fd, mode) == -1)
 | 
						|
	goto err;
 | 
						|
      /* Don't close (fd); */
 | 
						|
      return sem;
 | 
						|
    }
 | 
						|
 | 
						|
exists:
 | 
						|
  /* Open the file and fetch the semaphore name. */
 | 
						|
  if ((fd = open (semname, O_RDWR | O_CLOEXEC)) < 0)
 | 
						|
    {
 | 
						|
      if (errno == ENOENT && (oflag & O_CREAT))
 | 
						|
	goto again;
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  /* Make certain initialization is complete */
 | 
						|
  for (i = 0; i < MAX_TRIES; i++)
 | 
						|
    {
 | 
						|
      if (stat64 (semname, &statbuff) == -1)
 | 
						|
	{
 | 
						|
	  if (errno == ENOENT && (oflag & O_CREAT))
 | 
						|
	    {
 | 
						|
	      close (fd);
 | 
						|
	      fd = -1;
 | 
						|
	      goto again;
 | 
						|
	    }
 | 
						|
	  goto err;
 | 
						|
	}
 | 
						|
      if ((statbuff.st_mode & S_IXUSR) == 0)
 | 
						|
	break;
 | 
						|
      sleep (1);
 | 
						|
    }
 | 
						|
  if (i == MAX_TRIES)
 | 
						|
    {
 | 
						|
      set_errno (ETIMEDOUT);
 | 
						|
      goto err;
 | 
						|
    }
 | 
						|
  if (file.lock (fd, sizeof sf))
 | 
						|
    goto err;
 | 
						|
  if (read (fd, &sf, sizeof sf) != sizeof sf)
 | 
						|
    goto err;
 | 
						|
  sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, sf.value, wasopen);
 | 
						|
  file.unlock (fd);
 | 
						|
  if (sem == SEM_FAILED)
 | 
						|
    goto err;
 | 
						|
  /* If wasopen is set, the semaphore was already opened and we already have
 | 
						|
     an open file descriptor pointing to the file.  This means, we have to
 | 
						|
     close the file descriptor created in this call.  It won't be stored
 | 
						|
     anywhere anyway. */
 | 
						|
  if (wasopen)
 | 
						|
    close (fd);
 | 
						|
  return sem;
 | 
						|
 | 
						|
err:
 | 
						|
  /* Don't let following function calls change errno */
 | 
						|
  save_errno save;
 | 
						|
 | 
						|
  file.unlock (fd);
 | 
						|
  if (created)
 | 
						|
    unlink (semname);
 | 
						|
  if (sem != SEM_FAILED)
 | 
						|
    semaphore::close (sem);
 | 
						|
  if (fd >= 0)
 | 
						|
    close (fd);
 | 
						|
  return SEM_FAILED;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
_sem_close (sem_t *sem, bool do_close)
 | 
						|
{
 | 
						|
  sem_finfo sf;
 | 
						|
  int fd, ret = -1;
 | 
						|
  ipc_flock file;
 | 
						|
 | 
						|
  if (semaphore::getinternal (sem, &fd, &sf.hash, &sf.luid, &sf.value) == -1)
 | 
						|
    return -1;
 | 
						|
  if (!file.lock (fd, sizeof sf)
 | 
						|
      && lseek64 (fd, 0LL, SEEK_SET) != (off_t) -1
 | 
						|
      && write (fd, &sf, sizeof sf) == sizeof sf)
 | 
						|
    ret = do_close ? semaphore::close (sem) : 0;
 | 
						|
 | 
						|
  /* Don't let following function calls change errno */
 | 
						|
  save_errno save;
 | 
						|
  file.unlock (fd);
 | 
						|
  close (fd);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
sem_close (sem_t *sem)
 | 
						|
{
 | 
						|
  return _sem_close (sem, true);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
sem_unlink (const char *name)
 | 
						|
{
 | 
						|
  size_t len = strlen (name);
 | 
						|
  char semname[ipc_names[semaphore].prefix_len + len];
 | 
						|
 | 
						|
  if (!check_path (semname, semaphore, name, len))
 | 
						|
    return -1;
 | 
						|
  if (unlink (semname) == -1)
 | 
						|
    return -1;
 | 
						|
  return 0;
 | 
						|
}
 |