1 /* shm.cc: XSI IPC interface for Cygwin.
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
10 #include <sys/queue.h>
16 #include "cygserver_shm.h"
20 #include "mmap_alloc.h"
22 /* __getpagesize is only available from libcygwin.a */
24 #define SHMLBA (wincap.allocation_granularity ())
27 * client_request_shm Constructors
30 client_request_shm::client_request_shm (int shmid
,
33 : client_request (CYGSERVER_REQUEST_SHM
, &_parameters
, sizeof (_parameters
))
35 _parameters
.in
.shmop
= SHMOP_shmat
;
36 ipc_set_proc_info (_parameters
.in
.ipcblk
);
38 _parameters
.in
.atargs
.shmid
= shmid
;
39 _parameters
.in
.atargs
.shmaddr
= shmaddr
;
40 _parameters
.in
.atargs
.shmflg
= shmflg
;
42 msglen (sizeof (_parameters
.in
));
45 client_request_shm::client_request_shm (int shmid
,
48 : client_request (CYGSERVER_REQUEST_SHM
, &_parameters
, sizeof (_parameters
))
50 _parameters
.in
.shmop
= SHMOP_shmctl
;
51 ipc_set_proc_info (_parameters
.in
.ipcblk
);
53 _parameters
.in
.ctlargs
.shmid
= shmid
;
54 _parameters
.in
.ctlargs
.cmd
= cmd
;
55 _parameters
.in
.ctlargs
.buf
= buf
;
57 msglen (sizeof (_parameters
.in
));
60 client_request_shm::client_request_shm (const void *shmaddr
)
61 : client_request (CYGSERVER_REQUEST_SHM
, &_parameters
, sizeof (_parameters
))
63 _parameters
.in
.shmop
= SHMOP_shmdt
;
64 ipc_set_proc_info (_parameters
.in
.ipcblk
);
66 _parameters
.in
.dtargs
.shmaddr
= shmaddr
;
68 msglen (sizeof (_parameters
.in
));
71 client_request_shm::client_request_shm (key_t key
,
74 : client_request (CYGSERVER_REQUEST_SHM
, &_parameters
, sizeof (_parameters
))
76 _parameters
.in
.shmop
= SHMOP_shmget
;
77 ipc_set_proc_info (_parameters
.in
.ipcblk
);
79 _parameters
.in
.getargs
.key
= key
;
80 _parameters
.in
.getargs
.size
= size
;
81 _parameters
.in
.getargs
.shmflg
= shmflg
;
83 msglen (sizeof (_parameters
.in
));
86 client_request_shm::client_request_shm (proc
*p1
)
87 : client_request (CYGSERVER_REQUEST_SHM
, &_parameters
, sizeof (_parameters
))
89 _parameters
.in
.shmop
= SHMOP_shmfork
;
90 ipc_set_proc_info (_parameters
.in
.ipcblk
, true);
92 _parameters
.in
.forkargs
= *p1
;
95 /* List of shmid's with file mapping HANDLE and size, returned by shmget. */
96 struct shm_shmid_list
{
97 SLIST_ENTRY (shm_shmid_list
) ssh_next
;
104 static SLIST_HEAD (, shm_shmid_list
) ssh_list
;
106 /* List of attached mappings, as returned by shmat. */
107 struct shm_attached_list
{
108 SLIST_ENTRY (shm_attached_list
) sph_next
;
110 shm_shmid_list
*parent
;
114 static SLIST_HEAD (, shm_attached_list
) sph_list
;
116 static NO_COPY SRWLOCK shm_lock
= SRWLOCK_INIT
;
117 #define SLIST_LOCK() (AcquireSRWLockExclusive (&shm_lock))
118 #define SLIST_UNLOCK() (ReleaseSRWLockExclusive (&shm_lock))
121 fixup_shms_after_fork ()
123 if (!SLIST_FIRST (&sph_list
))
125 pinfo
p (myself
->ppid
);
126 proc parent
= { myself
->ppid
, p
->dwProcessId
, p
->uid
, p
->gid
};
128 client_request_shm
request (&parent
);
129 if (request
.make_request () == -1 || request
.retval () == -1)
131 syscall_printf ("-1 [%d] = fixup_shms_after_fork ()", request
.error_code ());
132 set_errno (request
.error_code ());
135 shm_attached_list
*sph_entry
;
136 /* Reconstruct map from list... */
137 SLIST_FOREACH (sph_entry
, &sph_list
, sph_next
)
140 vm_object_t ptr
= sph_entry
->ptr
;
141 SIZE_T viewsize
= sph_entry
->parent
->size
;
142 status
= NtMapViewOfSection (sph_entry
->parent
->hdl
, NtCurrentProcess (),
143 &ptr
, 0, sph_entry
->parent
->size
, NULL
,
144 &viewsize
, ViewShare
, 0, sph_entry
->access
);
145 if (!NT_SUCCESS (status
) || ptr
!= sph_entry
->ptr
)
146 api_fatal ("fixup_shms_after_fork: NtMapViewOfSection (%p), status %y. Terminating.",
147 sph_entry
->ptr
, status
);
153 * XSI shmaphore API. These are exported by the DLL.
157 shmat (int shmid
, const void *shmaddr
, int shmflg
)
159 syscall_printf ("shmat (shmid = %d, shmaddr = %p, shmflg = %y)",
160 shmid
, shmaddr
, shmflg
);
163 shm_shmid_list
*ssh_entry
;
164 SLIST_FOREACH (ssh_entry
, &ssh_list
, ssh_next
)
166 if (ssh_entry
->shmid
== shmid
)
171 /* The shmid is unknown to this process so far. Try to get it from
172 the server if it exists. Use special internal call to shmget,
173 which interprets the key as a shmid and only returns a valid
174 shmid if one exists. Since shmctl inserts a new entry for this
175 shmid into ssh_list automatically, we just have to go through
176 that list again. If that still fails, well, bad luck. */
178 if (shmid
&& shmget ((key_t
) shmid
, 0, IPC_KEY_IS_SHMID
) != -1)
181 SLIST_FOREACH (ssh_entry
, &ssh_list
, ssh_next
)
183 if (ssh_entry
->shmid
== shmid
)
193 /* Early increment ref counter. This allows further actions to run with
194 unlocked lists, because shmdt or shmctl(IPC_RMID) won't delete this
196 ++ssh_entry
->ref_count
;
199 vm_object_t attach_va
= NULL
;
202 if (shmflg
& SHM_RND
)
203 attach_va
= (vm_object_t
)((vm_offset_t
)shmaddr
& ~(SHMLBA
-1));
205 attach_va
= (vm_object_t
)shmaddr
;
206 /* Don't even bother to call anything if shmaddr is NULL or
208 if (!attach_va
|| (vm_offset_t
)attach_va
% SHMLBA
)
211 --ssh_entry
->ref_count
;
215 /* Try allocating memory before calling cygserver. */
216 shm_attached_list
*sph_entry
= new (shm_attached_list
);
220 --ssh_entry
->ref_count
;
224 SIZE_T viewsize
= ssh_entry
->size
;
225 vm_object_t ptr
= mmap_alloc
.alloc (NULL
, viewsize
, false);
227 ULONG access
= (shmflg
& SHM_RDONLY
) ? PAGE_READONLY
: PAGE_READWRITE
;
228 status
= NtMapViewOfSection (ssh_entry
->hdl
, NtCurrentProcess (), &ptr
, 0,
229 ssh_entry
->size
, NULL
, &viewsize
, ViewShare
,
230 MEM_TOP_DOWN
, access
);
231 if (!NT_SUCCESS (status
))
233 __seterrno_from_nt_status (status
);
235 --ssh_entry
->ref_count
;
238 /* Use returned ptr address as is, so it's stored using the exact value
240 client_request_shm
request (shmid
, ptr
, shmflg
& ~SHM_RND
);
241 if (request
.make_request () == -1 || request
.ptrval () == NULL
)
243 syscall_printf ("-1 [%d] = shmat ()", request
.error_code ());
244 UnmapViewOfFile (ptr
);
246 set_errno (request
.error_code ());
247 --ssh_entry
->ref_count
;
250 sph_entry
->ptr
= ptr
;
251 sph_entry
->parent
= ssh_entry
;
252 sph_entry
->access
= access
;
254 SLIST_INSERT_HEAD (&sph_list
, sph_entry
, sph_next
);
260 shmctl (int shmid
, int cmd
, struct shmid_ds
*buf
)
262 syscall_printf ("shmctl (shmid = %d, cmd = %d, buf = %p)",
266 client_request_shm
request (shmid
, cmd
, buf
);
267 if (request
.make_request () == -1 || request
.retval () == -1)
269 syscall_printf ("-1 [%d] = shmctl ()", request
.error_code ());
270 set_errno (request
.error_code ());
276 shm_shmid_list
*ssh_entry
, *ssh_next_entry
;
278 SLIST_FOREACH_SAFE (ssh_entry
, &ssh_list
, ssh_next
, ssh_next_entry
)
280 if (ssh_entry
->shmid
== shmid
)
282 /* Remove this entry from the list and close the handle
283 only if it's not in use anymore. */
284 if (ssh_entry
->ref_count
<= 0)
286 SLIST_REMOVE (&ssh_list
, ssh_entry
, shm_shmid_list
,
288 CloseHandle (ssh_entry
->hdl
);
296 return request
.retval ();
304 shmdt (const void *shmaddr
)
306 syscall_printf ("shmdt (shmaddr = %p)", shmaddr
);
307 client_request_shm
request (shmaddr
);
308 if (request
.make_request () == -1 || request
.retval () == -1)
310 syscall_printf ("-1 [%d] = shmdt ()", request
.error_code ());
311 set_errno (request
.error_code ());
314 shm_attached_list
*sph_entry
, *sph_next_entry
;
315 /* Remove map from list... */
317 SLIST_FOREACH_SAFE (sph_entry
, &sph_list
, sph_next
, sph_next_entry
)
319 if (sph_entry
->ptr
== shmaddr
)
321 SLIST_REMOVE (&sph_list
, sph_entry
, shm_attached_list
, sph_next
);
322 /* ...unmap view... */
323 UnmapViewOfFile (sph_entry
->ptr
);
324 /* ...and, if this was the last reference to this shared section... */
325 shm_shmid_list
*ssh_entry
= sph_entry
->parent
;
326 if (--ssh_entry
->ref_count
<= 0)
328 /* ...delete parent entry and close handle. */
329 SLIST_REMOVE (&ssh_list
, ssh_entry
, shm_shmid_list
, ssh_next
);
330 CloseHandle (ssh_entry
->hdl
);
338 return request
.retval ();
342 shmget (key_t key
, size_t size
, int shmflg
)
344 syscall_printf ("shmget (key = %U, size = %d, shmflg = %y)",
346 /* Try allocating memory before calling cygserver. */
347 shm_shmid_list
*ssh_new_entry
= new (shm_shmid_list
);
353 client_request_shm
request (key
, size
, shmflg
);
354 if (request
.make_request () == -1 || request
.retval () == -1)
356 syscall_printf ("-1 [%d] = shmget ()", request
.error_code ());
357 delete ssh_new_entry
;
358 set_errno (request
.error_code ());
361 int shmid
= request
.retval (); /* Shared mem ID */
362 vm_object_t hdl
= request
.objval (); /* HANDLE associated with it. */
363 shm_shmid_list
*ssh_entry
;
365 SLIST_FOREACH (ssh_entry
, &ssh_list
, ssh_next
)
367 if (ssh_entry
->shmid
== shmid
)
369 /* We already maintain an entry for this shmid. That means,
370 the hdl returned by cygserver is a superfluous duplicate
371 of the original hdl maintained by cygserver. We can safely
374 delete ssh_new_entry
;
379 /* We arrive here only if shmid is a new one for this process. Add the
380 shmid and hdl value to the list. */
381 ssh_new_entry
->shmid
= shmid
;
382 ssh_new_entry
->hdl
= hdl
;
383 /* Fetch segment size from server. If this is an already existing segment,
384 the size value in this shmget call is supposed to be meaningless. */
385 struct shmid_ds stat
;
386 client_request_shm
stat_req (shmid
, IPC_STAT
, &stat
);
387 if (stat_req
.make_request () == -1 || stat_req
.retval () == -1)
388 ssh_new_entry
->size
= size
;
390 ssh_new_entry
->size
= stat
.shm_segsz
;
391 ssh_new_entry
->ref_count
= 0;
392 SLIST_INSERT_HEAD (&ssh_list
, ssh_new_entry
, ssh_next
);