Cygwin: (mostly) drop NT4 and Samba < 3.0 support
[newlib-cygwin.git] / winsup / cygwin / shm.cc
blobb60ce4cde9b9faab19a3f438758fe6e6a3ad3448
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
7 details. */
9 #include "winsup.h"
10 #include <sys/queue.h>
11 #include <unistd.h>
13 #include "pinfo.h"
14 #include "sigproc.h"
16 #include "cygserver_shm.h"
17 #include "cygtls.h"
18 #include "sync.h"
19 #include "ntdll.h"
20 #include "mmap_alloc.h"
22 /* __getpagesize is only available from libcygwin.a */
23 #undef SHMLBA
24 #define SHMLBA (wincap.allocation_granularity ())
27 * client_request_shm Constructors
30 client_request_shm::client_request_shm (int shmid,
31 const void *shmaddr,
32 int shmflg)
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,
46 int cmd,
47 struct shmid_ds *buf)
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,
72 size_t size,
73 int shmflg)
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;
98 int shmid;
99 vm_object_t hdl;
100 size_t size;
101 int ref_count;
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;
109 vm_object_t ptr;
110 shm_shmid_list *parent;
111 ULONG access;
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))
124 return 0;
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 ());
133 return 0;
135 shm_attached_list *sph_entry;
136 /* Reconstruct map from list... */
137 SLIST_FOREACH (sph_entry, &sph_list, sph_next)
139 NTSTATUS status;
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);
149 return 0;
153 * XSI shmaphore API. These are exported by the DLL.
156 extern "C" void *
157 shmat (int shmid, const void *shmaddr, int shmflg)
159 syscall_printf ("shmat (shmid = %d, shmaddr = %p, shmflg = %y)",
160 shmid, shmaddr, shmflg);
162 SLIST_LOCK ();
163 shm_shmid_list *ssh_entry;
164 SLIST_FOREACH (ssh_entry, &ssh_list, ssh_next)
166 if (ssh_entry->shmid == shmid)
167 break;
169 if (!ssh_entry)
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. */
177 SLIST_UNLOCK ();
178 if (shmid && shmget ((key_t) shmid, 0, IPC_KEY_IS_SHMID) != -1)
180 SLIST_LOCK ();
181 SLIST_FOREACH (ssh_entry, &ssh_list, ssh_next)
183 if (ssh_entry->shmid == shmid)
184 goto inc_ref_count;
186 SLIST_UNLOCK ();
188 /* Invalid shmid */
189 set_errno (EINVAL);
190 return (void *) -1;
192 inc_ref_count:
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
195 ssh_entry. */
196 ++ssh_entry->ref_count;
197 SLIST_UNLOCK ();
199 vm_object_t attach_va = NULL;
200 if (shmaddr)
202 if (shmflg & SHM_RND)
203 attach_va = (vm_object_t)((vm_offset_t)shmaddr & ~(SHMLBA-1));
204 else
205 attach_va = (vm_object_t)shmaddr;
206 /* Don't even bother to call anything if shmaddr is NULL or
207 not aligned. */
208 if (!attach_va || (vm_offset_t)attach_va % SHMLBA)
210 set_errno (EINVAL);
211 --ssh_entry->ref_count;
212 return (void *) -1;
215 /* Try allocating memory before calling cygserver. */
216 shm_attached_list *sph_entry = new (shm_attached_list);
217 if (!sph_entry)
219 set_errno (ENOMEM);
220 --ssh_entry->ref_count;
221 return (void *) -1;
223 NTSTATUS status;
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);
234 delete sph_entry;
235 --ssh_entry->ref_count;
236 return (void *) -1;
238 /* Use returned ptr address as is, so it's stored using the exact value
239 in cygserver. */
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);
245 delete sph_entry;
246 set_errno (request.error_code ());
247 --ssh_entry->ref_count;
248 return (void *) -1;
250 sph_entry->ptr = ptr;
251 sph_entry->parent = ssh_entry;
252 sph_entry->access = access;
253 SLIST_LOCK ();
254 SLIST_INSERT_HEAD (&sph_list, sph_entry, sph_next);
255 SLIST_UNLOCK ();
256 return ptr;
259 extern "C" int
260 shmctl (int shmid, int cmd, struct shmid_ds *buf)
262 syscall_printf ("shmctl (shmid = %d, cmd = %d, buf = %p)",
263 shmid, cmd, buf);
264 __try
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 ());
271 __leave;
273 if (cmd == IPC_RMID)
275 /* Cleanup */
276 shm_shmid_list *ssh_entry, *ssh_next_entry;
277 SLIST_LOCK ();
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,
287 ssh_next);
288 CloseHandle (ssh_entry->hdl);
289 delete ssh_entry;
291 break;
294 SLIST_UNLOCK ();
296 return request.retval ();
298 __except (EFAULT) {}
299 __endtry
300 return -1;
303 extern "C" int
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 ());
312 return -1;
314 shm_attached_list *sph_entry, *sph_next_entry;
315 /* Remove map from list... */
316 SLIST_LOCK ();
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);
331 delete ssh_entry;
333 delete sph_entry;
334 break;
337 SLIST_UNLOCK ();
338 return request.retval ();
341 extern "C" int
342 shmget (key_t key, size_t size, int shmflg)
344 syscall_printf ("shmget (key = %U, size = %d, shmflg = %y)",
345 key, size, shmflg);
346 /* Try allocating memory before calling cygserver. */
347 shm_shmid_list *ssh_new_entry = new (shm_shmid_list);
348 if (!ssh_new_entry)
350 set_errno (ENOMEM);
351 return -1;
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 ());
359 return -1;
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;
364 SLIST_LOCK ();
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
372 delete it. */
373 CloseHandle (hdl);
374 delete ssh_new_entry;
375 SLIST_UNLOCK ();
376 return shmid;
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;
389 else
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);
393 SLIST_UNLOCK ();
394 return shmid;