897 lines
22 KiB
C++
897 lines
22 KiB
C++
/* 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));
|
|
}
|