OSDN Git Service

* path.cc (conv_path_list): Take cygwin_conv_path_t as third parameter.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / shm.cc
1 /* shm.cc: XSI IPC interface for Cygwin.
2
3    Copyright 2003, 2004, 2007, 2009 Red Hat, Inc.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #include "winsup.h"
12 #include <sys/queue.h>
13 #include <unistd.h>
14
15 #include "pinfo.h"
16 #include "sigproc.h"
17
18 #include "cygserver_shm.h"
19 #include "cygtls.h"
20 #include "sync.h"
21 #include "ntdll.h"
22
23 /*
24  * client_request_shm Constructors
25  */
26
27 client_request_shm::client_request_shm (int shmid,
28                                         const void *shmaddr,
29                                         int shmflg)
30   : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
31 {
32   _parameters.in.shmop = SHMOP_shmat;
33   ipc_set_proc_info (_parameters.in.ipcblk);
34
35   _parameters.in.atargs.shmid = shmid;
36   _parameters.in.atargs.shmaddr = shmaddr;
37   _parameters.in.atargs.shmflg = shmflg;
38
39   msglen (sizeof (_parameters.in));
40 }
41
42 client_request_shm::client_request_shm (int shmid,
43                                         int cmd,
44                                         struct shmid_ds *buf)
45   : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
46 {
47   _parameters.in.shmop = SHMOP_shmctl;
48   ipc_set_proc_info (_parameters.in.ipcblk);
49
50    _parameters.in.ctlargs.shmid = shmid;
51    _parameters.in.ctlargs.cmd = cmd;
52    _parameters.in.ctlargs.buf = buf;
53
54   msglen (sizeof (_parameters.in));
55 }
56
57 client_request_shm::client_request_shm (const void *shmaddr)
58   : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
59 {
60   _parameters.in.shmop = SHMOP_shmdt;
61   ipc_set_proc_info (_parameters.in.ipcblk);
62
63   _parameters.in.dtargs.shmaddr = shmaddr;
64
65   msglen (sizeof (_parameters.in));
66 }
67
68 client_request_shm::client_request_shm (key_t key,
69                                         size_t size,
70                                         int shmflg)
71   : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
72 {
73   _parameters.in.shmop = SHMOP_shmget;
74   ipc_set_proc_info (_parameters.in.ipcblk);
75
76   _parameters.in.getargs.key = key;
77   _parameters.in.getargs.size = size;
78   _parameters.in.getargs.shmflg = shmflg;
79
80   msglen (sizeof (_parameters.in));
81 }
82
83 client_request_shm::client_request_shm (proc *p1)
84   : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
85 {
86   _parameters.in.shmop = SHMOP_shmfork;
87   ipc_set_proc_info (_parameters.in.ipcblk);
88
89   _parameters.in.forkargs = *p1;
90 }
91
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;
95   int shmid;
96   vm_object_t hdl;
97   size_t size;
98   int ref_count;
99 };
100
101 static SLIST_HEAD (, shm_shmid_list) ssh_list;
102
103 /* List of attached mappings, as returned by shmat. */
104 struct shm_attached_list {
105   SLIST_ENTRY (shm_attached_list) sph_next;
106   vm_object_t ptr;
107   shm_shmid_list *parent;
108   ULONG access;
109 };
110
111 static SLIST_HEAD (, shm_attached_list) sph_list;
112
113 static NO_COPY muto shm_guard;
114 #define SLIST_LOCK()    (shm_guard.init ("shm_guard")->acquire ())
115 #define SLIST_UNLOCK()  (shm_guard.release ())
116
117 int __stdcall
118 fixup_shms_after_fork ()
119 {
120   if (!SLIST_FIRST (&sph_list))
121     return 0;
122   pinfo p (myself->ppid);
123   proc parent = { myself->ppid, p->dwProcessId, p->uid, p->gid };
124
125   client_request_shm request (&parent);
126   if (request.make_request () == -1 || request.retval () == -1)
127     {
128       syscall_printf ("-1 [%d] = fixup_shms_after_fork ()", request.error_code ());
129       set_errno (request.error_code ());
130       return 0;
131     }
132   shm_attached_list *sph_entry;
133   /* Reconstruct map from list... */
134   SLIST_FOREACH (sph_entry, &sph_list, sph_next)
135     {
136       NTSTATUS status;
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);
145     }
146   return 0;
147 }
148
149 /*
150  * XSI shmaphore API.  These are exported by the DLL.
151  */
152
153 extern "C" void *
154 shmat (int shmid, const void *shmaddr, int shmflg)
155 {
156   syscall_printf ("shmat (shmid = %d, shmaddr = %p, shmflg = 0x%x)",
157                   shmid, shmaddr, shmflg);
158
159   SLIST_LOCK ();
160   shm_shmid_list *ssh_entry;
161   SLIST_FOREACH (ssh_entry, &ssh_list, ssh_next)
162     {
163       if (ssh_entry->shmid == shmid)
164         break;
165     }
166   if (!ssh_entry)
167     {
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)
175         {
176           SLIST_FOREACH (ssh_entry, &ssh_list, ssh_next)
177             {
178               if (ssh_entry->shmid == shmid)
179                 break;
180             }
181         }
182       if (!ssh_entry)
183         {
184           /* Invalid shmid */
185           set_errno (EINVAL);
186           SLIST_UNLOCK ();
187           return (void *) -1;
188         }
189     }
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
192      ssh_entry. */
193   ++ssh_entry->ref_count;
194   SLIST_UNLOCK ();
195
196   vm_object_t attach_va = NULL;
197   if (shmaddr)
198     {
199       if (shmflg & SHM_RND)
200         attach_va = (vm_object_t)((vm_offset_t)shmaddr & ~(SHMLBA-1));
201       else
202         attach_va = (vm_object_t)shmaddr;
203       /* Don't even bother to call anything if shmaddr is NULL or
204          not aligned. */
205       if (!attach_va || (vm_offset_t)attach_va % SHMLBA)
206         {
207           set_errno (EINVAL);
208           --ssh_entry->ref_count;
209           return (void *) -1;
210         }
211     }
212   /* Try allocating memory before calling cygserver. */
213   shm_attached_list *sph_entry = new (shm_attached_list);
214   if (!sph_entry)
215     {
216       set_errno (ENOMEM);
217       --ssh_entry->ref_count;
218       return (void *) -1;
219     }
220   NTSTATUS status;
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))
228     {
229       __seterrno_from_nt_status (status);
230       delete sph_entry;
231       --ssh_entry->ref_count;
232       return (void *) -1;
233     }
234   /* Use returned ptr address as is, so it's stored using the exact value
235      in cygserver. */
236   client_request_shm request (shmid, ptr, shmflg & ~SHM_RND);
237   if (request.make_request () == -1 || request.ptrval () == NULL)
238     {
239       syscall_printf ("-1 [%d] = shmat ()", request.error_code ());
240       UnmapViewOfFile (ptr);
241       delete sph_entry;
242       set_errno (request.error_code ());
243       --ssh_entry->ref_count;
244       if (request.error_code () == ENOSYS)
245         raise (SIGSYS);
246       return (void *) -1;
247     }
248   sph_entry->ptr = ptr;
249   sph_entry->parent = ssh_entry;
250   sph_entry->access = access;
251   SLIST_LOCK ();
252   SLIST_INSERT_HEAD (&sph_list, sph_entry, sph_next);
253   SLIST_UNLOCK ();
254   return ptr;
255 }
256
257 extern "C" int
258 shmctl (int shmid, int cmd, struct shmid_ds *buf)
259 {
260   syscall_printf ("shmctl (shmid = %d, cmd = %d, buf = 0x%x)",
261                   shmid, cmd, buf);
262   myfault efault;
263   if (efault.faulted (EFAULT))
264     return -1;
265   client_request_shm request (shmid, cmd, buf);
266   if (request.make_request () == -1 || request.retval () == -1)
267     {
268       syscall_printf ("-1 [%d] = shmctl ()", request.error_code ());
269       set_errno (request.error_code ());
270       if (request.error_code () == ENOSYS)
271         raise (SIGSYS);
272       return -1;
273     }
274   if (cmd == IPC_RMID)
275     {
276       /* Cleanup */
277       shm_shmid_list *ssh_entry, *ssh_next_entry;
278       SLIST_LOCK ();
279       SLIST_FOREACH_SAFE (ssh_entry, &ssh_list, ssh_next, ssh_next_entry)
280         {
281           if (ssh_entry->shmid == shmid)
282             {
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)
286                 {
287                   SLIST_REMOVE (&ssh_list, ssh_entry, shm_shmid_list, ssh_next);
288                   CloseHandle (ssh_entry->hdl);
289                   delete ssh_entry;
290                 }
291               break;
292             }
293         }
294       SLIST_UNLOCK ();
295     }
296   return request.retval ();
297 }
298
299 extern "C" int
300 shmdt (const void *shmaddr)
301 {
302   syscall_printf ("shmdt (shmaddr = %p)", shmaddr);
303   client_request_shm request (shmaddr);
304   if (request.make_request () == -1 || request.retval () == -1)
305     {
306       syscall_printf ("-1 [%d] = shmdt ()", request.error_code ());
307       set_errno (request.error_code ());
308       if (request.error_code () == ENOSYS)
309         raise (SIGSYS);
310       return -1;
311     }
312   shm_attached_list *sph_entry, *sph_next_entry;
313   /* Remove map from list... */
314   SLIST_LOCK ();
315   SLIST_FOREACH_SAFE (sph_entry, &sph_list, sph_next, sph_next_entry)
316     {
317       if (sph_entry->ptr == shmaddr)
318         {
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)
325             {
326               /* ...delete parent entry and close handle. */
327               SLIST_REMOVE (&ssh_list, ssh_entry, shm_shmid_list, ssh_next);
328               CloseHandle (ssh_entry->hdl);
329               delete ssh_entry;
330             }
331           delete sph_entry;
332           break;
333         }
334     }
335   SLIST_UNLOCK ();
336   return request.retval ();
337 }
338
339 extern "C" int
340 shmget (key_t key, size_t size, int shmflg)
341 {
342   syscall_printf ("shmget (key = %U, size = %d, shmflg = 0x%x)",
343                   key, size, shmflg);
344   /* Try allocating memory before calling cygserver. */
345   shm_shmid_list *ssh_new_entry = new (shm_shmid_list);
346   if (!ssh_new_entry)
347     {
348       set_errno (ENOMEM);
349       return -1;
350     }
351   client_request_shm request (key, size, shmflg);
352   if (request.make_request () == -1 || request.retval () == -1)
353     {
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)
358         raise (SIGSYS);
359       return -1;
360     }
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)
366     {
367       if (ssh_entry->shmid == shmid)
368         {
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;
377         }
378     }
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);
386   SLIST_UNLOCK ();
387   return shmid;
388 }