/* shm.cc: Single unix specification IPC interface for Cygwin. Copyright 2002 Red Hat, Inc. Written by Conrad Scott . Based on code by Robert Collins . 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 #include #include #include #include #include "cygerrno.h" #include "safe_memory.h" #include "sigproc.h" #include "cygserver_ipc.h" #include "cygserver_shm.h" /*---------------------------------------------------------------------------* * with_strerr () *---------------------------------------------------------------------------*/ #define with_strerr(MSG, ACTION) \ do \ { \ const DWORD lasterr = GetLastError (); \ char *MSG = NULL; \ if (!FormatMessage ((FORMAT_MESSAGE_ALLOCATE_BUFFER \ | FORMAT_MESSAGE_FROM_SYSTEM \ | FORMAT_MESSAGE_IGNORE_INSERTS), \ NULL, \ lasterr, \ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), \ reinterpret_cast(&MSG), \ 0, \ NULL)) \ { \ MSG = static_cast \ (LocalAlloc (LMEM_FIXED, 24)); /* Big enough. */ \ if (!MSG) \ { \ system_printf (("failure in LocalAlloc(LMEM_FIXED, 16): " \ "error = %lu"), \ GetLastError ()); \ } \ else \ { \ snprintf (MSG, 24, "error = %lu", lasterr); \ } \ } \ SetLastError (lasterr); \ { ACTION; } \ if (MSG && !LocalFree (MSG)) \ { \ system_printf ("failed to free memory at %p, error = %lu", \ MSG, GetLastError ()); \ } \ SetLastError (lasterr); \ } while (false) /*---------------------------------------------------------------------------* * class client_shmmgr * * A singleton class. *---------------------------------------------------------------------------*/ #define shmmgr (client_shmmgr::instance ()) class client_shmmgr { private: class segment_t { public: const int shmid; const void *const shmaddr; const int shmflg; HANDLE hFileMap; // Updated by fixup_shms_after_fork (). segment_t *next; segment_t (const int shmid, const void *const shmaddr, const int shmflg, const HANDLE hFileMap) : shmid (shmid), shmaddr (shmaddr), shmflg (shmflg), hFileMap (hFileMap), next (NULL) {} }; public: static client_shmmgr & instance (); void *shmat (int shmid, const void *, int shmflg); int shmctl (int shmid, int cmd, struct shmid_ds *); int shmdt (const void *); int shmget (key_t, size_t, int shmflg); int fixup_shms_after_fork (); private: static NO_COPY client_shmmgr *_instance; CRITICAL_SECTION _segments_lock; static segment_t *_segments_head; // List of attached segs by shmaddr. static long _shmat_cnt; // No. of attached segs; for info. only. client_shmmgr (); ~client_shmmgr (); // Undefined (as this class is a singleton): client_shmmgr (const client_shmmgr &); client_shmmgr & operator= (const client_shmmgr &); segment_t *find (const void *, segment_t **previous = NULL); void *attach (int shmid, const void *, int shmflg, HANDLE & hFileMap); segment_t *new_segment (int shmid, const void *, int shmflg, HANDLE); }; /* static */ NO_COPY client_shmmgr *client_shmmgr::_instance; /* The following two variables must be inherited by child processes * since they are used by fixup_shms_after_fork () to re-attach to the * parent's shm segments. */ /* static */ client_shmmgr::segment_t *client_shmmgr::_segments_head; /* static */ long client_shmmgr::_shmat_cnt; /*---------------------------------------------------------------------------* * client_shmmgr::instance () *---------------------------------------------------------------------------*/ client_shmmgr & client_shmmgr::instance () { if (!_instance) _instance = safe_new0 (client_shmmgr); assert (_instance); return *_instance; } /*---------------------------------------------------------------------------* * client_shmmgr::shmat () *---------------------------------------------------------------------------*/ void * client_shmmgr::shmat (const int shmid, const void *const shmaddr, const int shmflg) { syscall_printf ("shmat (shmid = %d, shmaddr = 0x%p, shmflg = 0%o)", shmid, shmaddr, shmflg); EnterCriticalSection (&_segments_lock); HANDLE hFileMap = NULL; void *const ptr = attach (shmid, shmaddr, shmflg, hFileMap); if (ptr) new_segment (shmid, ptr, shmflg, hFileMap); LeaveCriticalSection (&_segments_lock); if (ptr) syscall_printf ("0x%p = shmat (shmid = %d, shmaddr = 0x%p, shmflg = 0%o)", ptr, shmid, shmaddr, shmflg); // else // See the syscall_printf in client_shmmgr::attach (). return (ptr ? ptr : (void *) -1); } /*---------------------------------------------------------------------------* * client_shmmgr::shmctl () *---------------------------------------------------------------------------*/ int client_shmmgr::shmctl (const int shmid, const int cmd, struct shmid_ds *const buf) { syscall_printf ("shmctl (shmid = %d, cmd = 0x%x, buf = 0x%p)", shmid, cmd, buf); // Check parameters and set up in parameters as required. const struct shmid_ds *in_buf = NULL; switch (cmd) { case IPC_SET: if (__check_invalid_read_ptr_errno (buf, sizeof (struct shmid_ds))) { syscall_printf (("-1 [EFAULT] = " "shmctl (shmid = %d, cmd = 0x%x, buf = 0x%p)"), shmid, cmd, buf); set_errno (EFAULT); return -1; } in_buf = buf; break; case IPC_STAT: case SHM_STAT: if (__check_null_invalid_struct_errno (buf, sizeof (struct shmid_ds))) { syscall_printf (("-1 [EFAULT] = " "shmctl (shmid = %d, cmd = 0x%x, buf = 0x%p)"), shmid, cmd, buf); set_errno (EFAULT); return -1; } break; case IPC_INFO: if (__check_null_invalid_struct_errno (buf, sizeof (struct shminfo))) { syscall_printf (("-1 [EFAULT] = " "shmctl (shmid = %d, cmd = 0x%x, buf = 0x%p)"), shmid, cmd, buf); set_errno (EFAULT); return -1; } break; case SHM_INFO: if (__check_null_invalid_struct_errno (buf, sizeof (struct shm_info))) { syscall_printf (("-1 [EFAULT] = " "shmctl (shmid = %d, cmd = 0x%x, buf = 0x%p)"), shmid, cmd, buf); set_errno (EFAULT); return -1; } break; } // Create and issue the command. client_request_shm request (shmid, cmd, in_buf); if (request.make_request () == -1 || request.error_code ()) { syscall_printf (("-1 [%d] = " "shmctl (shmid = %d, cmd = 0x%x, buf = 0x%p)"), request.error_code (), shmid, cmd, buf); set_errno (request.error_code ()); return -1; } // Some commands require special processing for their out parameters. int result = 0; switch (cmd) { case IPC_STAT: *buf = request.ds (); break; case IPC_INFO: *(struct shminfo *) buf = request.shminfo (); break; case SHM_STAT: // ipcs(8) i'face. result = request.shmid (); *buf = request.ds (); break; case SHM_INFO: // ipcs(8) i'face. result = request.shmid (); *(struct shm_info *) buf = request.shm_info (); break; } syscall_printf ("%d = shmctl (shmid = %d, cmd = 0x%x, buf = 0x%p)", result, shmid, cmd, buf); return result; } /*---------------------------------------------------------------------------* * client_shmmgr::shmdt () * * According to Posix, the only error condition for this system call * is EINVAL if shmaddr is not the address of the start of an attached * shared memory segment. Given that, all other errors just generate * tracing noise. *---------------------------------------------------------------------------*/ int client_shmmgr::shmdt (const void *const shmaddr) { syscall_printf ("shmdt (shmaddr = 0x%p)", shmaddr); EnterCriticalSection (&_segments_lock); segment_t *previous = NULL; segment_t *const segptr = find (shmaddr, &previous); if (!segptr) { LeaveCriticalSection (&_segments_lock); syscall_printf ("-1 [EINVAL] = shmdt (shmaddr = 0x%p)", shmaddr); set_errno (EINVAL); return -1; } assert (previous ? previous->next == segptr : _segments_head == segptr); if (previous) previous->next = segptr->next; else _segments_head = segptr->next; LeaveCriticalSection (&_segments_lock); const long cnt = InterlockedDecrement (&_shmat_cnt); assert (cnt >= 0); if (!UnmapViewOfFile ((void *) shmaddr)) with_strerr (msg, syscall_printf (("failed to unmap view " "[shmid = %d, handle = %p, shmaddr = %p]:" "%s"), segptr->shmid, segptr->hFileMap, shmaddr, msg)); assert (segptr->hFileMap); if (!CloseHandle (segptr->hFileMap)) with_strerr (msg, syscall_printf (("failed to close file map handle " "[shmid = %d, handle = %p]:" "%s"), segptr->shmid, segptr->hFileMap, msg)); client_request_shm request (segptr->shmid); if (request.make_request () == -1 || request.error_code ()) syscall_printf ("shmdt request failed [shmid = %d, handle = %p]: %s", segptr->shmid, segptr->hFileMap, strerror (request.error_code ())); safe_delete (segment_t, segptr); syscall_printf ("0 = shmdt (shmaddr = 0x%p)", shmaddr); return 0; } /*---------------------------------------------------------------------------* * client_shmmgr::shmget () *---------------------------------------------------------------------------*/ int client_shmmgr::shmget (const key_t key, const size_t size, const int shmflg) { syscall_printf ("shmget (key = 0x%016X, size = %u, shmflg = 0%o)", key, size, shmflg); client_request_shm request (key, size, shmflg); if (request.make_request () == -1 || request.error_code ()) { syscall_printf (("-1 [%d] = " "shmget (key = 0x%016X, size = %u, shmflg = 0%o)"), request.error_code (), key, size, shmflg); set_errno (request.error_code ()); return -1; } syscall_printf (("%d = shmget (key = 0x%016X, size = %u, shmflg = 0%o)"), request.shmid (), key, size, shmflg); return request.shmid (); } /*---------------------------------------------------------------------------* * client_shmmgr::fixup_shms_after_fork () * * The hFileMap handles are non-inheritable: so they have to be * re-acquired from cygserver. * * Nb. This routine need not be thread-safe as it is only called at startup. *---------------------------------------------------------------------------*/ int client_shmmgr::fixup_shms_after_fork () { debug_printf ("re-attaching to shm segments: %d attached", _shmat_cnt); { int length = 0; for (segment_t *segptr = _segments_head; segptr; segptr = segptr->next) length += 1; if (_shmat_cnt != length) { system_printf (("state inconsistent: " "_shmat_cnt = %d, length of segments list = %d"), _shmat_cnt, length); return 1; } } for (segment_t *segptr = _segments_head; segptr; segptr = segptr->next) if (!attach (segptr->shmid, segptr->shmaddr, segptr->shmflg & ~SHM_RND, segptr->hFileMap)) { system_printf ("fatal error re-attaching to shm segment %d", segptr->shmid); return 1; } if (_shmat_cnt) debug_printf ("re-attached all %d shm segments", _shmat_cnt); return 0; } /*---------------------------------------------------------------------------* * client_shmmgr::client_shmmgr () *---------------------------------------------------------------------------*/ client_shmmgr::client_shmmgr () { InitializeCriticalSection (&_segments_lock); } /*---------------------------------------------------------------------------* * client_shmmgr::~client_shmmgr () *---------------------------------------------------------------------------*/ client_shmmgr::~client_shmmgr () { DeleteCriticalSection (&_segments_lock); } /*---------------------------------------------------------------------------* * client_shmmgr::find () *---------------------------------------------------------------------------*/ client_shmmgr::segment_t * client_shmmgr::find (const void *const shmaddr, segment_t **previous) { if (previous) *previous = NULL; for (segment_t *segptr = _segments_head; segptr; segptr = segptr->next) if (segptr->shmaddr == shmaddr) return segptr; else if (segptr->shmaddr > shmaddr) // The list is sorted by shmaddr. return NULL; else if (previous) *previous = segptr; return NULL; } /*---------------------------------------------------------------------------* * client_shmmgr::attach () * * The body of shmat (), also used by fixup_shms_after_fork (). *---------------------------------------------------------------------------*/ void * client_shmmgr::attach (const int shmid, const void *shmaddr, const int shmflg, HANDLE & hFileMap) { client_request_shm request (shmid, shmflg); if (request.make_request () == -1 || request.error_code ()) { syscall_printf (("-1 [%d] = " "shmat (shmid = %d, shmaddr = 0x%p, shmflg = 0%o)"), request.error_code (), shmid, shmaddr, shmflg); set_errno (request.error_code ()); return NULL; } int result = 0; const DWORD access = (shmflg & SHM_RDONLY) ? FILE_MAP_READ : FILE_MAP_WRITE; if (shmaddr && (shmflg & SHM_RND)) shmaddr = (char *) shmaddr - ((ssize_t) shmaddr % SHMLBA); void *const ptr = MapViewOfFileEx (request.hFileMap (), access, 0, 0, 0, (void *) shmaddr); if (!ptr) { with_strerr (msg, syscall_printf (("failed to map view " "[shmid = %d, handle = %p, shmaddr = %p]:" "%s"), shmid, request.hFileMap (), shmaddr, msg)); result = EINVAL; // FIXME } else if (shmaddr && ptr != shmaddr) { syscall_printf (("failed to map view at requested address " "[shmid = %d, handle = %p]: " "requested address = %p, mapped address = %p"), shmid, request.hFileMap (), shmaddr, ptr); result = EINVAL; // FIXME } if (result != 0) { if (!CloseHandle (request.hFileMap ())) with_strerr (msg, syscall_printf (("failed to close file map handle " "[shmid = %d, handle = %p]:" "%s"), shmid, request.hFileMap (), msg)); client_request_shm dt_req (shmid); if (dt_req.make_request () == -1 || dt_req.error_code ()) syscall_printf ("shmdt request failed [shmid = %d, handle = %p]: %s", shmid, request.hFileMap (), strerror (dt_req.error_code ())); set_errno (result); return NULL; } hFileMap = request.hFileMap (); return ptr; } /*---------------------------------------------------------------------------* * client_shmmgr::new_segment () * * Allocate a new segment for the given shmid, file map and address * and insert into the segment map. *---------------------------------------------------------------------------*/ client_shmmgr::segment_t * client_shmmgr::new_segment (const int shmid, const void *const shmaddr, const int shmflg, const HANDLE hFileMap) { assert (ipc_ext2int_subsys (shmid) == IPC_SHMOP); assert (hFileMap); assert (shmaddr); segment_t *previous = NULL; // Insert pointer. const segment_t *const tmp = find (shmaddr, &previous); assert (!tmp); assert (previous \ ? (!previous->next || previous->next->shmaddr > shmaddr) \ : (!_segments_head || _segments_head->shmaddr > shmaddr)); segment_t *const segptr = safe_new (segment_t, shmid, shmaddr, shmflg, hFileMap); assert (segptr); if (previous) { segptr->next = previous->next; previous->next = segptr; } else { segptr->next = _segments_head; _segments_head = segptr; } const long cnt = InterlockedIncrement (&_shmat_cnt); assert (cnt > 0); return segptr; } /*---------------------------------------------------------------------------* * shmat () *---------------------------------------------------------------------------*/ extern "C" void * shmat (const int shmid, const void *const shmaddr, const int shmflg) { sigframe thisframe (mainthread); return shmmgr.shmat (shmid, shmaddr, shmflg); } /*---------------------------------------------------------------------------* * shmctl () *---------------------------------------------------------------------------*/ extern "C" int shmctl (const int shmid, const int cmd, struct shmid_ds *const buf) { sigframe thisframe (mainthread); return shmmgr.shmctl (shmid, cmd, buf); } /*---------------------------------------------------------------------------* * shmdt () *---------------------------------------------------------------------------*/ extern "C" int shmdt (const void *const shmaddr) { sigframe thisframe (mainthread); return shmmgr.shmdt (shmaddr); } /*---------------------------------------------------------------------------* * shmget () *---------------------------------------------------------------------------*/ extern "C" int shmget (const key_t key, const size_t size, const int shmflg) { sigframe thisframe (mainthread); return shmmgr.shmget (key, size, shmflg); } /*---------------------------------------------------------------------------* * fixup_shms_after_fork () *---------------------------------------------------------------------------*/ int __stdcall fixup_shms_after_fork () { return shmmgr.fixup_shms_after_fork (); } /*---------------------------------------------------------------------------* * client_request_shm::client_request_shm () *---------------------------------------------------------------------------*/ client_request_shm::client_request_shm (const int shmid, const int shmflg) : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) { _parameters.in.shmop = SHMOP_shmat; _parameters.in.shmid = shmid; _parameters.in.shmflg = shmflg; _parameters.in.cygpid = getpid (); _parameters.in.winpid = GetCurrentProcessId (); _parameters.in.uid = geteuid (); _parameters.in.gid = getegid (); msglen (sizeof (_parameters.in)); } /*---------------------------------------------------------------------------* * client_request_shm::client_request_shm () *---------------------------------------------------------------------------*/ client_request_shm::client_request_shm (const int shmid, const int cmd, const struct shmid_ds *const buf) : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) { _parameters.in.shmop = SHMOP_shmctl; _parameters.in.shmid = shmid; _parameters.in.cmd = cmd; if (buf) _parameters.in.ds = *buf; _parameters.in.cygpid = getpid (); _parameters.in.winpid = GetCurrentProcessId (); _parameters.in.uid = geteuid (); _parameters.in.gid = getegid (); msglen (sizeof (_parameters.in)); } /*---------------------------------------------------------------------------* * client_request_shm::client_request_shm () *---------------------------------------------------------------------------*/ client_request_shm::client_request_shm (const int shmid) : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) { _parameters.in.shmop = SHMOP_shmdt; _parameters.in.shmid = shmid; _parameters.in.cygpid = getpid (); _parameters.in.winpid = GetCurrentProcessId (); _parameters.in.uid = geteuid (); _parameters.in.gid = getegid (); msglen (sizeof (_parameters.in)); } /*---------------------------------------------------------------------------* * client_request_shm::client_request_shm () *---------------------------------------------------------------------------*/ client_request_shm::client_request_shm (const key_t key, const size_t size, const int shmflg) : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) { _parameters.in.shmop = SHMOP_shmget; _parameters.in.key = key; _parameters.in.size = size; _parameters.in.shmflg = shmflg; _parameters.in.cygpid = getpid (); _parameters.in.winpid = GetCurrentProcessId (); _parameters.in.uid = geteuid (); _parameters.in.gid = getegid (); msglen (sizeof (_parameters.in)); }