1 /* shm.cc: XSI IPC interface for Cygwin.
3 Copyright 2003, 2004, 2007, 2009 Red Hat, Inc.
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
12 #include <sys/queue.h>
18 #include "cygserver_shm.h"
24 * client_request_shm Constructors
27 client_request_shm::client_request_shm (int shmid,
30 : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
32 _parameters.in.shmop = SHMOP_shmat;
33 ipc_set_proc_info (_parameters.in.ipcblk);
35 _parameters.in.atargs.shmid = shmid;
36 _parameters.in.atargs.shmaddr = shmaddr;
37 _parameters.in.atargs.shmflg = shmflg;
39 msglen (sizeof (_parameters.in));
42 client_request_shm::client_request_shm (int shmid,
45 : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
47 _parameters.in.shmop = SHMOP_shmctl;
48 ipc_set_proc_info (_parameters.in.ipcblk);
50 _parameters.in.ctlargs.shmid = shmid;
51 _parameters.in.ctlargs.cmd = cmd;
52 _parameters.in.ctlargs.buf = buf;
54 msglen (sizeof (_parameters.in));
57 client_request_shm::client_request_shm (const void *shmaddr)
58 : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
60 _parameters.in.shmop = SHMOP_shmdt;
61 ipc_set_proc_info (_parameters.in.ipcblk);
63 _parameters.in.dtargs.shmaddr = shmaddr;
65 msglen (sizeof (_parameters.in));
68 client_request_shm::client_request_shm (key_t key,
71 : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
73 _parameters.in.shmop = SHMOP_shmget;
74 ipc_set_proc_info (_parameters.in.ipcblk);
76 _parameters.in.getargs.key = key;
77 _parameters.in.getargs.size = size;
78 _parameters.in.getargs.shmflg = shmflg;
80 msglen (sizeof (_parameters.in));
83 client_request_shm::client_request_shm (proc *p1)
84 : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
86 _parameters.in.shmop = SHMOP_shmfork;
87 ipc_set_proc_info (_parameters.in.ipcblk);
89 _parameters.in.forkargs = *p1;
92 /* List of shmid's with file mapping HANDLE and size, returned by shmget. */
93 struct shm_shmid_list {
94 SLIST_ENTRY (shm_shmid_list) ssh_next;
101 static SLIST_HEAD (, shm_shmid_list) ssh_list;
103 /* List of attached mappings, as returned by shmat. */
104 struct shm_attached_list {
105 SLIST_ENTRY (shm_attached_list) sph_next;
107 shm_shmid_list *parent;
111 static SLIST_HEAD (, shm_attached_list) sph_list;
113 static NO_COPY muto shm_guard;
114 #define SLIST_LOCK() (shm_guard.init ("shm_guard")->acquire ())
115 #define SLIST_UNLOCK() (shm_guard.release ())
118 fixup_shms_after_fork ()
120 if (!SLIST_FIRST (&sph_list))
122 pinfo p (myself->ppid);
123 proc parent = { myself->ppid, p->dwProcessId, p->uid, p->gid };
125 client_request_shm request (&parent);
126 if (request.make_request () == -1 || request.retval () == -1)
128 syscall_printf ("-1 [%d] = fixup_shms_after_fork ()", request.error_code ());
129 set_errno (request.error_code ());
132 shm_attached_list *sph_entry;
133 /* Reconstruct map from list... */
134 SLIST_FOREACH (sph_entry, &sph_list, sph_next)
137 vm_object_t ptr = sph_entry->ptr;
138 ULONG viewsize = sph_entry->parent->size;
139 status = NtMapViewOfSection (sph_entry->parent->hdl, NtCurrentProcess (),
140 &ptr, 0, sph_entry->parent->size, NULL,
141 &viewsize, ViewShare, 0, sph_entry->access);
142 if (!NT_SUCCESS (status) || ptr != sph_entry->ptr)
143 api_fatal ("fixup_shms_after_fork: NtMapViewOfSection (%p), status %p. Terminating.",
144 sph_entry->ptr, status);
150 * XSI shmaphore API. These are exported by the DLL.
154 shmat (int shmid, const void *shmaddr, int shmflg)
156 syscall_printf ("shmat (shmid = %d, shmaddr = %p, shmflg = 0x%x)",
157 shmid, shmaddr, shmflg);
160 shm_shmid_list *ssh_entry;
161 SLIST_FOREACH (ssh_entry, &ssh_list, ssh_next)
163 if (ssh_entry->shmid == shmid)
168 /* The shmid is unknown to this process so far. Try to get it from
169 the server if it exists. Use special internal call to shmget,
170 which interprets the key as a shmid and only returns a valid
171 shmid if one exists. Since shmctl inserts a new entry for this
172 shmid into ssh_list automatically, we just have to go through
173 that list again. If that still fails, well, bad luck. */
174 if (shmid && shmget ((key_t) shmid, 0, IPC_KEY_IS_SHMID) != -1)
176 SLIST_FOREACH (ssh_entry, &ssh_list, ssh_next)
178 if (ssh_entry->shmid == shmid)
190 /* Early increment ref counter. This allows further actions to run with
191 unlocked lists, because shmdt or shmctl(IPC_RMID) won't delete this
193 ++ssh_entry->ref_count;
196 vm_object_t attach_va = NULL;
199 if (shmflg & SHM_RND)
200 attach_va = (vm_object_t)((vm_offset_t)shmaddr & ~(SHMLBA-1));
202 attach_va = (vm_object_t)shmaddr;
203 /* Don't even bother to call anything if shmaddr is NULL or
205 if (!attach_va || (vm_offset_t)attach_va % SHMLBA)
208 --ssh_entry->ref_count;
212 /* Try allocating memory before calling cygserver. */
213 shm_attached_list *sph_entry = new (shm_attached_list);
217 --ssh_entry->ref_count;
221 vm_object_t ptr = NULL;
222 ULONG viewsize = ssh_entry->size;
223 ULONG access = (shmflg & SHM_RDONLY) ? PAGE_READONLY : PAGE_READWRITE;
224 status = NtMapViewOfSection (ssh_entry->hdl, NtCurrentProcess (), &ptr, 0,
225 ssh_entry->size, NULL, &viewsize, ViewShare,
226 MEM_TOP_DOWN, access);
227 if (!NT_SUCCESS (status))
229 __seterrno_from_nt_status (status);
231 --ssh_entry->ref_count;
234 /* Use returned ptr address as is, so it's stored using the exact value
236 client_request_shm request (shmid, ptr, shmflg & ~SHM_RND);
237 if (request.make_request () == -1 || request.ptrval () == NULL)
239 syscall_printf ("-1 [%d] = shmat ()", request.error_code ());
240 UnmapViewOfFile (ptr);
242 set_errno (request.error_code ());
243 --ssh_entry->ref_count;
244 if (request.error_code () == ENOSYS)
248 sph_entry->ptr = ptr;
249 sph_entry->parent = ssh_entry;
250 sph_entry->access = access;
252 SLIST_INSERT_HEAD (&sph_list, sph_entry, sph_next);
258 shmctl (int shmid, int cmd, struct shmid_ds *buf)
260 syscall_printf ("shmctl (shmid = %d, cmd = %d, buf = 0x%x)",
263 if (efault.faulted (EFAULT))
265 client_request_shm request (shmid, cmd, buf);
266 if (request.make_request () == -1 || request.retval () == -1)
268 syscall_printf ("-1 [%d] = shmctl ()", request.error_code ());
269 set_errno (request.error_code ());
270 if (request.error_code () == ENOSYS)
277 shm_shmid_list *ssh_entry, *ssh_next_entry;
279 SLIST_FOREACH_SAFE (ssh_entry, &ssh_list, ssh_next, ssh_next_entry)
281 if (ssh_entry->shmid == shmid)
283 /* Remove this entry from the list and close the handle
284 only if it's not in use anymore. */
285 if (ssh_entry->ref_count <= 0)
287 SLIST_REMOVE (&ssh_list, ssh_entry, shm_shmid_list, ssh_next);
288 CloseHandle (ssh_entry->hdl);
296 return request.retval ();
300 shmdt (const void *shmaddr)
302 syscall_printf ("shmdt (shmaddr = %p)", shmaddr);
303 client_request_shm request (shmaddr);
304 if (request.make_request () == -1 || request.retval () == -1)
306 syscall_printf ("-1 [%d] = shmdt ()", request.error_code ());
307 set_errno (request.error_code ());
308 if (request.error_code () == ENOSYS)
312 shm_attached_list *sph_entry, *sph_next_entry;
313 /* Remove map from list... */
315 SLIST_FOREACH_SAFE (sph_entry, &sph_list, sph_next, sph_next_entry)
317 if (sph_entry->ptr == shmaddr)
319 SLIST_REMOVE (&sph_list, sph_entry, shm_attached_list, sph_next);
320 /* ...unmap view... */
321 UnmapViewOfFile (sph_entry->ptr);
322 /* ...and, if this was the last reference to this shared section... */
323 shm_shmid_list *ssh_entry = sph_entry->parent;
324 if (--ssh_entry->ref_count <= 0)
326 /* ...delete parent entry and close handle. */
327 SLIST_REMOVE (&ssh_list, ssh_entry, shm_shmid_list, ssh_next);
328 CloseHandle (ssh_entry->hdl);
336 return request.retval ();
340 shmget (key_t key, size_t size, int shmflg)
342 syscall_printf ("shmget (key = %U, size = %d, shmflg = 0x%x)",
344 /* Try allocating memory before calling cygserver. */
345 shm_shmid_list *ssh_new_entry = new (shm_shmid_list);
351 client_request_shm request (key, size, shmflg);
352 if (request.make_request () == -1 || request.retval () == -1)
354 syscall_printf ("-1 [%d] = shmget ()", request.error_code ());
355 delete ssh_new_entry;
356 set_errno (request.error_code ());
357 if (request.error_code () == ENOSYS)
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 ssh_new_entry->size = size;
384 ssh_new_entry->ref_count = 0;
385 SLIST_INSERT_HEAD (&ssh_list, ssh_new_entry, ssh_next);