897 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			897 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin.
 | 
						|
 | 
						|
   Copyright 2002 Red Hat, Inc.
 | 
						|
 | 
						|
   Written by Conrad Scott <conrad.scott@dsl.pipex.com>.
 | 
						|
   Based on code by Robert Collins <robert.collins@hotmail.com>.
 | 
						|
 | 
						|
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 "woutsup.h"
 | 
						|
 | 
						|
#include <errno.h>
 | 
						|
#include <pthread.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <string.h>
 | 
						|
#include <time.h>
 | 
						|
 | 
						|
#include "cygserver_ipc.h"
 | 
						|
#include "cygserver_shm.h"
 | 
						|
#include "security.h"
 | 
						|
 | 
						|
#include "cygwin/cygserver.h"
 | 
						|
#include "cygwin/cygserver_process.h"
 | 
						|
#include "cygwin/cygserver_transport.h"
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * class server_shmmgr
 | 
						|
 *
 | 
						|
 * A singleton class.
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
#define shmmgr (server_shmmgr::instance ())
 | 
						|
 | 
						|
class server_shmmgr
 | 
						|
{
 | 
						|
private:
 | 
						|
  class attach_t
 | 
						|
  {
 | 
						|
  public:
 | 
						|
    class process *const _client;
 | 
						|
    unsigned int _refcnt;
 | 
						|
 | 
						|
    attach_t *_next;
 | 
						|
 | 
						|
    attach_t (class process *const client)
 | 
						|
      : _client (client),
 | 
						|
	_refcnt (0),
 | 
						|
	_next (NULL)
 | 
						|
    {}
 | 
						|
  };
 | 
						|
 | 
						|
  class segment_t
 | 
						|
  {
 | 
						|
  private:
 | 
						|
    // Bits for the _flg field.
 | 
						|
    enum { IS_DELETED = 0x01 };
 | 
						|
 | 
						|
  public:
 | 
						|
    const int _intid;
 | 
						|
    const int _shmid;
 | 
						|
    struct shmid_ds _ds;
 | 
						|
 | 
						|
    segment_t *_next;
 | 
						|
 | 
						|
    segment_t (const key_t key, const int intid, const HANDLE hFileMap);
 | 
						|
    ~segment_t ();
 | 
						|
 | 
						|
    bool is_deleted () const
 | 
						|
    {
 | 
						|
      return _flg & IS_DELETED;
 | 
						|
    }
 | 
						|
 | 
						|
    bool is_pending_delete () const
 | 
						|
    {
 | 
						|
      return !_ds.shm_nattch && is_deleted ();
 | 
						|
    }
 | 
						|
 | 
						|
    void mark_deleted ()
 | 
						|
    {
 | 
						|
      assert (!is_deleted ());
 | 
						|
 | 
						|
      _flg |= IS_DELETED;
 | 
						|
    }
 | 
						|
 | 
						|
    int attach (class process *, HANDLE & hFileMap);
 | 
						|
    int detach (class process *);
 | 
						|
 | 
						|
  private:
 | 
						|
    static long _sequence;
 | 
						|
 | 
						|
    int _flg;
 | 
						|
    const HANDLE _hFileMap;
 | 
						|
    attach_t *_attach_head;	// A list sorted by winpid;
 | 
						|
 | 
						|
    attach_t *find (const class process *, attach_t **previous = NULL);
 | 
						|
  };
 | 
						|
 | 
						|
  class cleanup_t : public cleanup_routine
 | 
						|
  {
 | 
						|
  public:
 | 
						|
    cleanup_t (const segment_t *const segptr)
 | 
						|
      : cleanup_routine (reinterpret_cast<void *> (segptr->_shmid))
 | 
						|
    {
 | 
						|
      assert (key ());
 | 
						|
    }
 | 
						|
 | 
						|
    int shmid () const { return reinterpret_cast<int> (key ()); }
 | 
						|
 | 
						|
    virtual void cleanup (class process *const client)
 | 
						|
    {
 | 
						|
      const int res = shmmgr.shmdt (shmid (), client);
 | 
						|
 | 
						|
      if (res != 0)
 | 
						|
	debug_printf ("process cleanup failed [shmid = %d]: %s",
 | 
						|
		      shmid (), strerror (-res));
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
public:
 | 
						|
  static server_shmmgr & instance ();
 | 
						|
 | 
						|
  int shmat (HANDLE & hFileMap,
 | 
						|
	     int shmid, int shmflg, class process *);
 | 
						|
  int shmctl (int & out_shmid, struct shmid_ds & out_ds,
 | 
						|
	      struct shminfo & out_shminfo, struct shm_info & out_shm_info,
 | 
						|
	      const int shmid, int cmd, const struct shmid_ds &,
 | 
						|
	      class process *);
 | 
						|
  int shmdt (int shmid, class process *);
 | 
						|
  int shmget (int & out_shmid, key_t, size_t, int shmflg, uid_t, gid_t,
 | 
						|
	      class process *);
 | 
						|
 | 
						|
private:
 | 
						|
  static server_shmmgr *_instance;
 | 
						|
  static pthread_once_t _instance_once;
 | 
						|
 | 
						|
  static void initialise_instance ();
 | 
						|
 | 
						|
  CRITICAL_SECTION _segments_lock;
 | 
						|
  segment_t *_segments_head;	// A list sorted by int_id.
 | 
						|
 | 
						|
  int _shm_ids;			// Number of shm segments (for ipcs(8)).
 | 
						|
  int _shm_tot;			// Total bytes of shm segments (for ipcs(8)).
 | 
						|
  int _shm_atts;		// Number of attached segments (for ipcs(8)).
 | 
						|
  int _intid_max;		// Highest intid yet allocated (for ipcs(8)).
 | 
						|
 | 
						|
  server_shmmgr ();
 | 
						|
  ~server_shmmgr ();
 | 
						|
 | 
						|
  // Undefined (as this class is a singleton):
 | 
						|
  server_shmmgr (const server_shmmgr &);
 | 
						|
  server_shmmgr & operator= (const server_shmmgr &);
 | 
						|
 | 
						|
  segment_t *find_by_key (key_t);
 | 
						|
  segment_t *find (int intid, segment_t **previous = NULL);
 | 
						|
 | 
						|
  int new_segment (key_t, size_t, int shmflg, pid_t, uid_t, gid_t);
 | 
						|
 | 
						|
  segment_t *new_segment (key_t, size_t, HANDLE);
 | 
						|
  void delete_segment (segment_t *);
 | 
						|
};
 | 
						|
 | 
						|
/* static */ long server_shmmgr::segment_t::_sequence = 0;
 | 
						|
 | 
						|
/* static */ server_shmmgr *server_shmmgr::_instance = NULL;
 | 
						|
/* static */ pthread_once_t server_shmmgr::_instance_once = PTHREAD_ONCE_INIT;
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::segment_t::segment_t ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
server_shmmgr::segment_t::segment_t (const key_t key,
 | 
						|
				     const int intid,
 | 
						|
				     const HANDLE hFileMap)
 | 
						|
  : _intid (intid),
 | 
						|
    _shmid (ipc_int2ext (intid, IPC_SHMOP, _sequence)),
 | 
						|
    _next (NULL),
 | 
						|
    _flg (0),
 | 
						|
    _hFileMap (hFileMap),
 | 
						|
    _attach_head (NULL)
 | 
						|
{
 | 
						|
  assert (0 <= _intid && _intid < SHMMNI);
 | 
						|
 | 
						|
  memset (&_ds, '\0', sizeof (_ds));
 | 
						|
  _ds.shm_perm.key = key;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::segment_t::~segment_t ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
server_shmmgr::segment_t::~segment_t ()
 | 
						|
{
 | 
						|
  assert (!_attach_head);
 | 
						|
 | 
						|
  if (!CloseHandle (_hFileMap))
 | 
						|
    syscall_printf ("failed to close file map [handle = 0x%x]: %E", _hFileMap);
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::segment_t::attach ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
int
 | 
						|
server_shmmgr::segment_t::attach (class process *const client,
 | 
						|
				  HANDLE & hFileMap)
 | 
						|
{
 | 
						|
  assert (client);
 | 
						|
 | 
						|
  if (!DuplicateHandle (GetCurrentProcess (),
 | 
						|
			_hFileMap,
 | 
						|
			client->handle (),
 | 
						|
			&hFileMap,
 | 
						|
			0,
 | 
						|
			FALSE, // bInheritHandle
 | 
						|
			DUPLICATE_SAME_ACCESS))
 | 
						|
    {
 | 
						|
      syscall_printf (("failed to duplicate handle for client "
 | 
						|
		       "[key = 0x%016llx, shmid = %d, handle = 0x%x]: %E"),
 | 
						|
		      _ds.shm_perm.key, _shmid, _hFileMap);
 | 
						|
 | 
						|
      return -EACCES;	// FIXME: Case analysis?
 | 
						|
    }
 | 
						|
 | 
						|
  _ds.shm_lpid  = client->cygpid ();
 | 
						|
  _ds.shm_nattch += 1;
 | 
						|
  _ds.shm_atime = time (NULL); // FIXME: sub-second times.
 | 
						|
 | 
						|
  attach_t *previous = NULL;
 | 
						|
  attach_t *attptr = find (client, &previous);
 | 
						|
 | 
						|
  if (!attptr)
 | 
						|
    {
 | 
						|
      attptr = safe_new (attach_t, client);
 | 
						|
 | 
						|
      if (previous)
 | 
						|
	{
 | 
						|
	  attptr->_next = previous->_next;
 | 
						|
	  previous->_next = attptr;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  attptr->_next = _attach_head;
 | 
						|
	  _attach_head = attptr;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  attptr->_refcnt += 1;
 | 
						|
 | 
						|
  cleanup_t *const cleanup = safe_new (cleanup_t, this);
 | 
						|
 | 
						|
  // FIXME: ::add should only fail if the process object is already
 | 
						|
  // cleaning up; but it can't be doing that since this thread has it
 | 
						|
  // locked.
 | 
						|
 | 
						|
  const bool result = client->add (cleanup);
 | 
						|
 | 
						|
  assert (result);
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::segment_t::detach ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
int
 | 
						|
server_shmmgr::segment_t::detach (class process *const client)
 | 
						|
{
 | 
						|
  attach_t *previous = NULL;
 | 
						|
  attach_t *const attptr = find (client, &previous);
 | 
						|
 | 
						|
  if (!attptr)
 | 
						|
    return -EINVAL;
 | 
						|
 | 
						|
  if (client->is_active ())
 | 
						|
    {
 | 
						|
      const cleanup_t key (this);
 | 
						|
 | 
						|
      if (!client->remove (&key))
 | 
						|
	syscall_printf (("failed to remove cleanup routine for %d(%lu) "
 | 
						|
			 "[shmid = %d]"),
 | 
						|
			client->cygpid (), client->winpid (),
 | 
						|
			_shmid);
 | 
						|
    }
 | 
						|
 | 
						|
  attptr->_refcnt -= 1;
 | 
						|
 | 
						|
  if (!attptr->_refcnt)
 | 
						|
    {
 | 
						|
      assert (previous ? previous->_next == attptr : _attach_head == attptr);
 | 
						|
 | 
						|
      if (previous)
 | 
						|
	previous->_next = attptr->_next;
 | 
						|
      else
 | 
						|
	_attach_head = attptr->_next;
 | 
						|
 | 
						|
      safe_delete (attptr);
 | 
						|
    }
 | 
						|
 | 
						|
  assert (_ds.shm_nattch > 0);
 | 
						|
 | 
						|
  _ds.shm_lpid  = client->cygpid ();
 | 
						|
  _ds.shm_nattch -= 1;
 | 
						|
  _ds.shm_dtime = time (NULL); // FIXME: sub-second times.
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::segment_t::find ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
server_shmmgr::attach_t *
 | 
						|
server_shmmgr::segment_t::find (const class process *const client,
 | 
						|
				attach_t **previous)
 | 
						|
{
 | 
						|
  if (previous)
 | 
						|
    *previous = NULL;
 | 
						|
 | 
						|
  // Nb. The _attach_head list is sorted by winpid.
 | 
						|
 | 
						|
  for (attach_t *attptr = _attach_head; attptr; attptr = attptr->_next)
 | 
						|
    if (attptr->_client == client)
 | 
						|
      return attptr;
 | 
						|
    else if (attptr->_client->winpid () > client->winpid ())
 | 
						|
      return NULL;
 | 
						|
    else if (previous)
 | 
						|
      *previous = attptr;
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::instance ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
/* static */ server_shmmgr &
 | 
						|
server_shmmgr::instance ()
 | 
						|
{
 | 
						|
  pthread_once (&_instance_once, &initialise_instance);
 | 
						|
 | 
						|
  assert (_instance);
 | 
						|
 | 
						|
  return *_instance;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::shmat ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
int
 | 
						|
server_shmmgr::shmat (HANDLE & hFileMap,
 | 
						|
		      const int shmid, const int shmflg,
 | 
						|
		      class process *const client)
 | 
						|
{
 | 
						|
  syscall_printf ("shmat (shmid = %d, shmflg = 0%o) for %d(%lu)",
 | 
						|
		  shmid, shmflg, client->cygpid (), client->winpid ());
 | 
						|
 | 
						|
  int result = 0;
 | 
						|
  EnterCriticalSection (&_segments_lock);
 | 
						|
 | 
						|
  segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP));
 | 
						|
 | 
						|
  if (!segptr)
 | 
						|
    result = -EINVAL;
 | 
						|
  else
 | 
						|
    result = segptr->attach (client, hFileMap);
 | 
						|
 | 
						|
  if (!result)
 | 
						|
    _shm_atts += 1;
 | 
						|
 | 
						|
  LeaveCriticalSection (&_segments_lock);
 | 
						|
 | 
						|
  if (result < 0)
 | 
						|
    syscall_printf (("-1 [%d] = shmat (shmid = %d, shmflg = 0%o) "
 | 
						|
		     "for %d(%lu)"),
 | 
						|
		    -result, shmid, shmflg,
 | 
						|
		    client->cygpid (), client->winpid ());
 | 
						|
  else
 | 
						|
    syscall_printf (("0x%x = shmat (shmid = %d, shmflg = 0%o) "
 | 
						|
		     "for %d(%lu)"),
 | 
						|
		    hFileMap, shmid, shmflg,
 | 
						|
		    client->cygpid (), client->winpid ());
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::shmctl ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
int
 | 
						|
server_shmmgr::shmctl (int & out_shmid,
 | 
						|
		       struct shmid_ds & out_ds,
 | 
						|
		       struct shminfo & out_shminfo,
 | 
						|
		       struct shm_info & out_shm_info,
 | 
						|
		       const int shmid, const int cmd,
 | 
						|
		       const struct shmid_ds & ds,
 | 
						|
		       class process *const client)
 | 
						|
{
 | 
						|
  syscall_printf ("shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)",
 | 
						|
		  shmid, cmd, client->cygpid (), client->winpid ());
 | 
						|
 | 
						|
  int result = 0;
 | 
						|
  EnterCriticalSection (&_segments_lock);
 | 
						|
 | 
						|
  switch (cmd)
 | 
						|
    {
 | 
						|
    case IPC_STAT:
 | 
						|
    case SHM_STAT:		// Uses intids rather than shmids.
 | 
						|
    case IPC_SET:
 | 
						|
    case IPC_RMID:
 | 
						|
      {
 | 
						|
	int intid;
 | 
						|
 | 
						|
	if (cmd == SHM_STAT)
 | 
						|
	  intid = shmid;
 | 
						|
	else
 | 
						|
	  intid = ipc_ext2int (shmid, IPC_SHMOP);
 | 
						|
 | 
						|
	segment_t *const segptr = find (intid);
 | 
						|
 | 
						|
	if (!segptr)
 | 
						|
	  result = -EINVAL;
 | 
						|
	else
 | 
						|
	  switch (cmd)
 | 
						|
	    {
 | 
						|
	    case IPC_STAT:
 | 
						|
	      out_ds = segptr->_ds;
 | 
						|
	      break;
 | 
						|
 | 
						|
	    case IPC_SET:
 | 
						|
	      segptr->_ds.shm_perm.uid = ds.shm_perm.uid;
 | 
						|
	      segptr->_ds.shm_perm.gid = ds.shm_perm.gid;
 | 
						|
	      segptr->_ds.shm_perm.mode = ds.shm_perm.mode & 0777;
 | 
						|
	      segptr->_ds.shm_lpid = client->cygpid ();
 | 
						|
	      segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times.
 | 
						|
	      break;
 | 
						|
 | 
						|
	    case IPC_RMID:
 | 
						|
	      if (segptr->is_deleted ())
 | 
						|
		result = -EIDRM;
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  segptr->mark_deleted ();
 | 
						|
		  if (segptr->is_pending_delete ())
 | 
						|
		    delete_segment (segptr);
 | 
						|
		}
 | 
						|
	      break;
 | 
						|
 | 
						|
	    case SHM_STAT:	// ipcs(8) i'face.
 | 
						|
	      out_ds = segptr->_ds;
 | 
						|
	      out_shmid = segptr->_shmid;
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case IPC_INFO:
 | 
						|
      out_shminfo.shmmax = SHMMAX;
 | 
						|
      out_shminfo.shmmin = SHMMIN;
 | 
						|
      out_shminfo.shmmni = SHMMNI;
 | 
						|
      out_shminfo.shmseg = SHMSEG;
 | 
						|
      out_shminfo.shmall = SHMALL;
 | 
						|
      break;
 | 
						|
 | 
						|
    case SHM_INFO:		// ipcs(8) i'face.
 | 
						|
      out_shmid = _intid_max;
 | 
						|
      out_shm_info.shm_ids = _shm_ids;
 | 
						|
      out_shm_info.shm_tot = _shm_tot;
 | 
						|
      out_shm_info.shm_atts = _shm_atts;
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      result = -EINVAL;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  LeaveCriticalSection (&_segments_lock);
 | 
						|
 | 
						|
  if (result < 0)
 | 
						|
    syscall_printf (("-1 [%d] = "
 | 
						|
		     "shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"),
 | 
						|
		    -result,
 | 
						|
		    shmid, cmd, client->cygpid (), client->winpid ());
 | 
						|
  else
 | 
						|
    syscall_printf (("%d = "
 | 
						|
		     "shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"),
 | 
						|
		    ((cmd == SHM_STAT || cmd == SHM_INFO)
 | 
						|
		     ? out_shmid
 | 
						|
		     : result),
 | 
						|
		    shmid, cmd, client->cygpid (), client->winpid ());
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::shmdt ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
int
 | 
						|
server_shmmgr::shmdt (const int shmid, class process *const client)
 | 
						|
{
 | 
						|
  syscall_printf ("shmdt (shmid = %d) for %d(%lu)",
 | 
						|
		  shmid, client->cygpid (), client->winpid ());
 | 
						|
 | 
						|
  int result = 0;
 | 
						|
  EnterCriticalSection (&_segments_lock);
 | 
						|
 | 
						|
  segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP));
 | 
						|
 | 
						|
  if (!segptr)
 | 
						|
    result = -EINVAL;
 | 
						|
  else
 | 
						|
    result = segptr->detach (client);
 | 
						|
 | 
						|
  if (!result)
 | 
						|
    _shm_atts -= 1;
 | 
						|
 | 
						|
  if (!result && segptr->is_pending_delete ())
 | 
						|
    delete_segment (segptr);
 | 
						|
 | 
						|
  LeaveCriticalSection (&_segments_lock);
 | 
						|
 | 
						|
  if (result < 0)
 | 
						|
    syscall_printf ("-1 [%d] = shmdt (shmid = %d) for %d(%lu)",
 | 
						|
		    -result, shmid, client->cygpid (), client->winpid ());
 | 
						|
  else
 | 
						|
    syscall_printf ("%d = shmdt (shmid = %d) for %d(%lu)",
 | 
						|
		    result, shmid, client->cygpid (), client->winpid ());
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::shmget ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
int
 | 
						|
server_shmmgr::shmget (int & out_shmid,
 | 
						|
		       const key_t key, const size_t size, const int shmflg,
 | 
						|
		       const uid_t uid, const gid_t gid,
 | 
						|
		       class process *const client)
 | 
						|
{
 | 
						|
  syscall_printf (("shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
 | 
						|
		   "for %d(%lu)"),
 | 
						|
		  key, size, shmflg,
 | 
						|
		  client->cygpid (), client->winpid ());
 | 
						|
 | 
						|
  int result = 0;
 | 
						|
  EnterCriticalSection (&_segments_lock);
 | 
						|
 | 
						|
  if (key == IPC_PRIVATE)
 | 
						|
    result = new_segment (key, size, shmflg,
 | 
						|
			  client->cygpid (), uid, gid);
 | 
						|
  else
 | 
						|
    {
 | 
						|
      segment_t *const segptr = find_by_key (key);
 | 
						|
 | 
						|
      if (!segptr)
 | 
						|
	if (shmflg & IPC_CREAT)
 | 
						|
	  result = new_segment (key, size, shmflg,
 | 
						|
				client->cygpid (), uid, gid);
 | 
						|
	else
 | 
						|
	  result = -ENOENT;
 | 
						|
      else if (segptr->is_deleted ())
 | 
						|
	result = -EIDRM;
 | 
						|
      else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL))
 | 
						|
	result = -EEXIST;
 | 
						|
      else if ((shmflg & ~(segptr->_ds.shm_perm.mode)) & 0777)
 | 
						|
	result = -EACCES;
 | 
						|
      else if (size && segptr->_ds.shm_segsz < size)
 | 
						|
	result = -EINVAL;
 | 
						|
      else
 | 
						|
	result = segptr->_shmid;
 | 
						|
    }
 | 
						|
 | 
						|
  LeaveCriticalSection (&_segments_lock);
 | 
						|
 | 
						|
  if (result >= 0)
 | 
						|
    {
 | 
						|
      out_shmid = result;
 | 
						|
      result = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  if (result < 0)
 | 
						|
    syscall_printf (("-1 [%d] = "
 | 
						|
		     "shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
 | 
						|
		     "for %d(%lu)"),
 | 
						|
		    -result,
 | 
						|
		    key, size, shmflg,
 | 
						|
		    client->cygpid (), client->winpid ());
 | 
						|
  else
 | 
						|
    syscall_printf (("%d = "
 | 
						|
		     "shmget (key = 0x%016llx, size = %u, shmflg = 0%o) "
 | 
						|
		     "for %d(%lu)"),
 | 
						|
		    out_shmid,
 | 
						|
		    key, size, shmflg,
 | 
						|
		    client->cygpid (), client->winpid ());
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::initialise_instance ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
/* static */ void
 | 
						|
server_shmmgr::initialise_instance ()
 | 
						|
{
 | 
						|
  assert (!_instance);
 | 
						|
 | 
						|
  _instance = safe_new0 (server_shmmgr);
 | 
						|
 | 
						|
  assert (_instance);
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::server_shmmgr ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
server_shmmgr::server_shmmgr ()
 | 
						|
  : _segments_head (NULL),
 | 
						|
    _shm_ids (0),
 | 
						|
    _shm_tot (0),
 | 
						|
    _shm_atts (0),
 | 
						|
    _intid_max (0)
 | 
						|
{
 | 
						|
  InitializeCriticalSection (&_segments_lock);
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::~server_shmmgr ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
server_shmmgr::~server_shmmgr ()
 | 
						|
{
 | 
						|
  DeleteCriticalSection (&_segments_lock);
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::find_by_key ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
server_shmmgr::segment_t *
 | 
						|
server_shmmgr::find_by_key (const key_t key)
 | 
						|
{
 | 
						|
  for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next)
 | 
						|
    if (segptr->_ds.shm_perm.key == key)
 | 
						|
      return segptr;
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::find ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
server_shmmgr::segment_t *
 | 
						|
server_shmmgr::find (const int intid, segment_t **previous)
 | 
						|
{
 | 
						|
  if (previous)
 | 
						|
    *previous = NULL;
 | 
						|
 | 
						|
  for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next)
 | 
						|
    if (segptr->_intid == intid)
 | 
						|
      return segptr;
 | 
						|
    else if (segptr->_intid > intid) // The list is sorted by intid.
 | 
						|
      return NULL;
 | 
						|
    else if (previous)
 | 
						|
      *previous = segptr;
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::new_segment ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
int
 | 
						|
server_shmmgr::new_segment (const key_t key,
 | 
						|
			    const size_t size,
 | 
						|
			    const int shmflg,
 | 
						|
			    const pid_t cygpid,
 | 
						|
			    const uid_t uid,
 | 
						|
			    const gid_t gid)
 | 
						|
{
 | 
						|
  if (size < SHMMIN || size > SHMMAX)
 | 
						|
      return -EINVAL;
 | 
						|
 | 
						|
  const HANDLE hFileMap = CreateFileMapping (INVALID_HANDLE_VALUE,
 | 
						|
					     NULL, PAGE_READWRITE,
 | 
						|
					     0, size,
 | 
						|
					     NULL);
 | 
						|
 | 
						|
  if (!hFileMap)
 | 
						|
    {
 | 
						|
      syscall_printf ("failed to create file mapping [size = %lu]: %E", size);
 | 
						|
      return -ENOMEM;		// FIXME
 | 
						|
    }
 | 
						|
 | 
						|
  segment_t *const segptr = new_segment (key, size, hFileMap);
 | 
						|
 | 
						|
  if (!segptr)
 | 
						|
    {
 | 
						|
      (void) CloseHandle (hFileMap);
 | 
						|
      return -ENOSPC;
 | 
						|
    }
 | 
						|
 | 
						|
  segptr->_ds.shm_perm.cuid = segptr->_ds.shm_perm.uid = uid;
 | 
						|
  segptr->_ds.shm_perm.cgid = segptr->_ds.shm_perm.gid = gid;
 | 
						|
  segptr->_ds.shm_perm.mode = shmflg & 0777;
 | 
						|
  segptr->_ds.shm_segsz = size;
 | 
						|
  segptr->_ds.shm_cpid = cygpid;
 | 
						|
  segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times.
 | 
						|
 | 
						|
  return segptr->_shmid;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::new_segment ()
 | 
						|
 *
 | 
						|
 * Allocate a new segment for the given key and file map with the
 | 
						|
 * lowest available intid and insert into the segment map.
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
server_shmmgr::segment_t *
 | 
						|
server_shmmgr::new_segment (const key_t key, const size_t size,
 | 
						|
			    const HANDLE hFileMap)
 | 
						|
{
 | 
						|
  // FIXME: Overflow risk.
 | 
						|
  if (_shm_tot + size > SHMALL)
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  int intid = 0;		// Next expected intid value.
 | 
						|
  segment_t *previous = NULL;	// Insert pointer.
 | 
						|
 | 
						|
  // Find first unallocated intid.
 | 
						|
  for (segment_t *segptr = _segments_head;
 | 
						|
       segptr && segptr->_intid == intid;
 | 
						|
       segptr = segptr->_next, intid++)
 | 
						|
    {
 | 
						|
      previous = segptr;
 | 
						|
    }
 | 
						|
 | 
						|
  /* By the time this condition is reached (given the default value of
 | 
						|
   * SHMMNI), the linear searches should all replaced by something
 | 
						|
   * just a *little* cleverer . . .
 | 
						|
   */
 | 
						|
  if (intid >= SHMMNI)
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  segment_t *const segptr = safe_new (segment_t, key, intid, hFileMap);
 | 
						|
 | 
						|
  assert (segptr);
 | 
						|
 | 
						|
  if (previous)
 | 
						|
    {
 | 
						|
      segptr->_next = previous->_next;
 | 
						|
      previous->_next = segptr;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      segptr->_next = _segments_head;
 | 
						|
      _segments_head = segptr;
 | 
						|
    }
 | 
						|
 | 
						|
  _shm_ids += 1;
 | 
						|
  _shm_tot += size;
 | 
						|
  if (intid > _intid_max)
 | 
						|
    _intid_max = intid;
 | 
						|
 | 
						|
  return segptr;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * server_shmmgr::delete_segment ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
void
 | 
						|
server_shmmgr::delete_segment (segment_t *const segptr)
 | 
						|
{
 | 
						|
  assert (segptr);
 | 
						|
  assert (segptr->is_pending_delete ());
 | 
						|
 | 
						|
  segment_t *previous = NULL;
 | 
						|
 | 
						|
  const segment_t *const tmp = find (segptr->_intid, &previous);
 | 
						|
 | 
						|
  assert (tmp == segptr);
 | 
						|
  assert (previous ? previous->_next == segptr : _segments_head == segptr);
 | 
						|
 | 
						|
  if (previous)
 | 
						|
    previous->_next = segptr->_next;
 | 
						|
  else
 | 
						|
    _segments_head = segptr->_next;
 | 
						|
 | 
						|
  assert (_shm_ids > 0);
 | 
						|
  _shm_ids -= 1;
 | 
						|
  _shm_tot -= segptr->_ds.shm_segsz;
 | 
						|
 | 
						|
  safe_delete (segptr);
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * client_request_shm::client_request_shm ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
client_request_shm::client_request_shm ()
 | 
						|
  : client_request (CYGSERVER_REQUEST_SHM,
 | 
						|
		    &_parameters, sizeof (_parameters))
 | 
						|
{
 | 
						|
  // verbose: syscall_printf ("created");
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*
 | 
						|
 * client_request_shm::serve ()
 | 
						|
 *---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
void
 | 
						|
client_request_shm::serve (transport_layer_base *const conn,
 | 
						|
			   process_cache *const cache)
 | 
						|
{
 | 
						|
  assert (conn);
 | 
						|
 | 
						|
  assert (!error_code ());
 | 
						|
 | 
						|
  if (msglen () != sizeof (_parameters.in))
 | 
						|
    {
 | 
						|
      syscall_printf ("bad request body length: expecting %lu bytes, got %lu",
 | 
						|
		      sizeof (_parameters), msglen ());
 | 
						|
      error_code (EINVAL);
 | 
						|
      msglen (0);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  // FIXME: Get a return code out of this and don't continue on error.
 | 
						|
  conn->impersonate_client ();
 | 
						|
 | 
						|
  class process *const client = cache->process (_parameters.in.cygpid,
 | 
						|
						_parameters.in.winpid);
 | 
						|
 | 
						|
  if (!client)
 | 
						|
    {
 | 
						|
      error_code (EAGAIN);
 | 
						|
      msglen (0);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  int result = -EINVAL;
 | 
						|
 | 
						|
  switch (_parameters.in.shmop)
 | 
						|
    {
 | 
						|
    case SHMOP_shmget:
 | 
						|
      result = shmmgr.shmget (_parameters.out.shmid,
 | 
						|
			      _parameters.in.key, _parameters.in.size,
 | 
						|
			      _parameters.in.shmflg,
 | 
						|
			      _parameters.in.uid, _parameters.in.gid,
 | 
						|
			      client);
 | 
						|
      break;
 | 
						|
 | 
						|
    case SHMOP_shmat:
 | 
						|
      result = shmmgr.shmat (_parameters.out.hFileMap,
 | 
						|
			     _parameters.in.shmid, _parameters.in.shmflg,
 | 
						|
			     client);
 | 
						|
      break;
 | 
						|
 | 
						|
    case SHMOP_shmdt:
 | 
						|
      result = shmmgr.shmdt (_parameters.in.shmid, client);
 | 
						|
      break;
 | 
						|
 | 
						|
    case SHMOP_shmctl:
 | 
						|
      result = shmmgr.shmctl (_parameters.out.shmid,
 | 
						|
			      _parameters.out.ds, _parameters.out.shminfo,
 | 
						|
			      _parameters.out.shm_info,
 | 
						|
			      _parameters.in.shmid, _parameters.in.cmd,
 | 
						|
			      _parameters.in.ds,
 | 
						|
			      client);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  client->release ();
 | 
						|
  conn->revert_to_self ();
 | 
						|
 | 
						|
  if (result < 0)
 | 
						|
    {
 | 
						|
      error_code (-result);
 | 
						|
      msglen (0);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    msglen (sizeof (_parameters.out));
 | 
						|
}
 |