Daemon-less io-layer code dump.
[mono.git] / mono / io-layer / shared.c
1 /*
2  * shared.c:  Shared memory handling, and daemon launching
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 /*
11  * Code to support inter-process sharing of handles.
12  *
13  * I thought of using an mmap()ed file for this.  If linuxthreads
14  * supported PTHREAD_PROCESS_SHARED I would have done; however without
15  * that pthread support the only other inter-process IPC
16  * synchronisation option is a sysV semaphore, and if I'm going to use
17  * that I may as well take advantage of sysV shared memory too.
18  * Actually, semaphores seem to be buggy, or I was using them
19  * incorrectly :-).  I've replaced the sysV semaphore with a shared
20  * integer controlled with Interlocked functions.  And I've since
21  * replaced that with a separate process to serialise access to the
22  * shared memory, to avoid the possibility of DOS by leaving the
23  * shared memory locked, and also to allow the shared memory to be
24  * cleaned up.
25  *
26  * mmap() files have the advantage of avoiding namespace collisions,
27  * but have the disadvantage of needing cleaning up, and also msync().
28  * sysV shared memory has a really stupid way of getting random key
29  * IDs, which can lead to collisions.
30  *
31  * Having tried sysv shm, I tested mmap() and found that MAP_SHARED
32  * makes msync() irrelevent, and both types need cleaning up.  Seeing
33  * as mmap() doesn't suffer from the bonkers method of allocating
34  * segments, it seems to be the best method.
35  *
36  * This shared memory is needed because w32 processes do not have the
37  * POSIX parent-child relationship, so a process handle is available
38  * to any other process to find out exit status.  Handles are
39  * destroyed when the last reference to them is closed.  New handles
40  * can be created for long lasting items such as processes or threads,
41  * and also for named synchronisation objects so long as these haven't
42  * been deleted by having the last referencing handle closed.
43  */
44
45
46 #include <config.h>
47 #include <glib.h>
48 #include <stdio.h>
49 #include <fcntl.h>
50 #include <unistd.h>
51 #include <sys/mman.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <errno.h>
55 #include <string.h>
56
57 #include <mono/io-layer/wapi.h>
58 #include <mono/io-layer/wapi-private.h>
59 #include <mono/io-layer/shared.h>
60
61 #undef DEBUG
62
63 static guchar *_wapi_shm_file (void)
64 {
65         static guchar file[_POSIX_PATH_MAX];
66         guchar *name = NULL, *filename, *dir, *wapi_dir;
67         gchar machine_name[256];
68
69         if (gethostname(machine_name, sizeof(machine_name)) != 0)
70                 machine_name[0] = '\0';
71         
72         /* Change the filename whenever the format of the contents
73          * changes
74          */
75         name = g_strdup_printf ("shared_data-%s-%d-%d",
76                                 machine_name, _WAPI_HANDLE_VERSION, 0);
77
78         /* I don't know how nfs affects mmap.  If mmap() of files on
79          * nfs mounts breaks, then there should be an option to set
80          * the directory.
81          */
82         wapi_dir = getenv ("MONO_SHARED_DIR");
83         if (wapi_dir == NULL) {
84                 filename = g_build_filename (g_get_home_dir (), ".wapi", name,
85                                              NULL);
86         } else {
87                 filename = g_build_filename (wapi_dir, ".wapi", name, NULL);
88         }
89         g_free (name);
90
91         g_snprintf (file, _POSIX_PATH_MAX, "%s", filename);
92         g_free (filename);
93                 
94         /* No need to check if the dir already exists or check
95          * mkdir() errors, because on any error the open() call will
96          * report the problem.
97          */
98         dir = g_path_get_dirname (file);
99         mkdir (dir, 0755);
100         g_free (dir);
101         
102         return(file);
103 }
104
105 static guchar *_wapi_fileshare_shm_file (void)
106 {
107         static guchar file[_POSIX_PATH_MAX];
108         guchar *name = NULL, *filename, *dir, *wapi_dir;
109         gchar machine_name[256];
110
111         if (gethostname(machine_name, sizeof(machine_name)) != 0)
112                 machine_name[0] = '\0';
113         
114         /* Change the filename whenever the format of the contents
115          * changes
116          */
117         name = g_strdup_printf ("shared_fileshare-%s-%d-%d",
118                                 machine_name, _WAPI_HANDLE_VERSION, 0);
119
120         /* I don't know how nfs affects mmap.  If mmap() of files on
121          * nfs mounts breaks, then there should be an option to set
122          * the directory.
123          */
124         wapi_dir = getenv ("MONO_SHARED_DIR");
125         if (wapi_dir == NULL) {
126                 filename = g_build_filename (g_get_home_dir (), ".wapi", name,
127                                              NULL);
128         } else {
129                 filename = g_build_filename (wapi_dir, ".wapi", name, NULL);
130         }
131         g_free (name);
132
133         g_snprintf (file, _POSIX_PATH_MAX, "%s", filename);
134         g_free (filename);
135                 
136         /* No need to check if the dir already exists or check
137          * mkdir() errors, because on any error the open() call will
138          * report the problem.
139          */
140         dir = g_path_get_dirname (file);
141         mkdir (dir, 0755);
142         g_free (dir);
143         
144         return(file);
145 }
146
147 static int _wapi_shm_file_open (const guchar *filename, guint32 wanted_size)
148 {
149         int fd;
150         struct stat statbuf;
151         int ret;
152         gboolean created = FALSE;
153         
154 try_again:
155         /* No O_CREAT yet, because we need to initialise the file if
156          * we have to create it.
157          */
158         fd = open (filename, O_RDWR, 0600);
159         if (fd == -1 && errno == ENOENT) {
160                 /* OK, its up to us to create it.  O_EXCL to avoid a
161                  * race condition where two processes can
162                  * simultaneously try and create the file
163                  */
164                 fd = open (filename, O_CREAT|O_EXCL|O_RDWR, 0600);
165                 if (fd == -1 && errno == EEXIST) {
166                         /* It's possible that the file was created in
167                          * between finding it didn't exist, and trying
168                          * to create it.  Just try opening it again
169                          */
170                         goto try_again;
171                 } else if (fd == -1) {
172                         g_critical ("%s: shared file [%s] open error: %s",
173                                     __func__, filename, g_strerror (errno));
174                         return(-1);
175                 } else {
176                         /* We created the file, so we need to expand
177                          * the file.
178                          *
179                          * (wanted_size-1, because we're about to
180                          * write the other byte to actually expand the
181                          * file.)
182                          */
183                         if (lseek (fd, wanted_size-1, SEEK_SET) == -1) {
184                                 g_critical ("%s: shared file [%s] lseek error: %s", __func__, filename, g_strerror (errno));
185                                 close (fd);
186                                 unlink (filename);
187                                 return(-1);
188                         }
189                         
190                         do {
191                                 ret = write (fd, "", 1);
192                         } while (ret == -1 && errno == EINTR);
193                                 
194                         if (ret == -1) {
195                                 g_critical ("%s: shared file [%s] write error: %s", __func__, filename, g_strerror (errno));
196                                 close (fd);
197                                 unlink (filename);
198                                 return(-1);
199                         }
200                         
201                         created = TRUE;
202
203                         /* The contents of the file is set to all
204                          * zero, because it is opened up with lseek,
205                          * so we don't need to do any more
206                          * initialisation here
207                          */
208                 }
209         } else if (fd == -1) {
210                 g_critical ("%s: shared file [%s] open error: %s", __func__,
211                             filename, g_strerror (errno));
212                 return(-1);
213         }
214         
215         /* Use stat to find the file size (instead of hard coding it)
216          * because we can expand the file later if needed (for more
217          * handles or scratch space.)
218          */
219         if (fstat (fd, &statbuf) == -1) {
220                 g_critical ("%s: fstat error: %s", __func__,
221                             g_strerror (errno));
222                 if (created == TRUE) {
223                         unlink (filename);
224                 }
225                 close (fd);
226                 return(-1);
227         }
228
229         if (statbuf.st_size < wanted_size) {
230                 close (fd);
231                 if (created == TRUE) {
232 #ifdef HAVE_LARGE_FILE_SUPPORT
233                         /* Keep gcc quiet... */
234                         g_critical ("%s: shared file [%s] is not big enough! (found %lld, need %d bytes)", __func__, filename, statbuf.st_size, wanted_size);
235 #else
236                         g_critical ("%s: shared file [%s] is not big enough! (found %ld, need %d bytes)", __func__, filename, statbuf.st_size, wanted_size);
237 #endif
238                         unlink (filename);
239                         return(-1);
240                 } else {
241                         /* We didn't create it, so just try opening it again */
242                         goto try_again;
243                 }
244         }
245         
246         return(fd);
247 }
248
249 /*
250  * _wapi_shm_attach:
251  * @success: Was it a success
252  *
253  * Attach to the shared memory file or create it if it did not exist.
254  * Returns the memory area the file was mmapped to.
255  */
256 gpointer _wapi_shm_attach (void)
257 {
258         gpointer shm_seg;
259         int fd;
260         struct stat statbuf;
261         guchar *filename=_wapi_shm_file ();
262         
263         fd=_wapi_shm_file_open (filename,
264                                 sizeof(struct _WapiHandleSharedLayout));
265         if(fd==-1) {
266                 g_critical ("%s: shared file [%s] open error", __func__,
267                             filename);
268                 return(NULL);
269         }
270         
271         if(fstat (fd, &statbuf)==-1) {
272                 g_critical ("%s: fstat error: %s", __func__,
273                             g_strerror (errno));
274                 close (fd);
275                 return(NULL);
276         }
277         
278         shm_seg=mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
279                       fd, 0);
280         if(shm_seg==MAP_FAILED) {
281                 g_critical ("%s: mmap error: %s", __func__,
282                             g_strerror (errno));
283                 close (fd);
284                 return(NULL);
285         }
286                 
287         close (fd);
288         return(shm_seg);
289 }
290
291 gpointer _wapi_fileshare_shm_attach (void)
292 {
293         gpointer shm_seg;
294         int fd;
295         struct stat statbuf;
296         guchar *filename=_wapi_fileshare_shm_file ();
297         
298         fd=_wapi_shm_file_open (filename, sizeof(struct _WapiFileShareLayout));
299         if(fd==-1) {
300                 g_critical ("%s: shared file [%s] open error", __func__,
301                             filename);
302                 return(NULL);
303         }
304         
305         if(fstat (fd, &statbuf)==-1) {
306                 g_critical ("%s: fstat error: %s", __func__,
307                             g_strerror (errno));
308                 close (fd);
309                 return(NULL);
310         }
311         
312         shm_seg=mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
313                       fd, 0);
314         if(shm_seg==MAP_FAILED) {
315                 g_critical ("%s: mmap error: %s", __func__,
316                             g_strerror (errno));
317                 close (fd);
318                 return(NULL);
319         }
320                 
321         close (fd);
322         return(shm_seg);
323 }