* mics.c: fix bug in _wapi_calc_timeout. It gave wrong values for
[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 #include <mono/io-layer/daemon-private.h>
61
62 #undef DEBUG
63
64 /* Define this to make it easier to run valgrind on the daemon.  Then
65  * the first process to start will turn into a daemon without forking
66  * (the debug utility mono/handles/hps is ideal for this.)
67  */
68 #undef VALGRINDING
69
70 guchar *_wapi_shm_file (_wapi_shm_t type, guint32 segment)
71 {
72         static guchar file[_POSIX_PATH_MAX];
73         guchar *name, *filename, *dir, *wapi_dir;
74         
75         /* Change the filename whenever the format of the contents
76          * changes
77          */
78         if(type==WAPI_SHM_DATA) {
79                 name=g_strdup_printf ("shared_data-%d-%d",
80                                       _WAPI_HANDLE_VERSION, segment);
81         } else if (type==WAPI_SHM_SCRATCH) {
82                 name=g_strdup_printf ("shared_scratch-%d-%d",
83                                       _WAPI_HANDLE_VERSION, segment);
84         } else {
85                 g_assert_not_reached ();
86         }
87
88         /* I don't know how nfs affects mmap.  If mmap() of files on
89          * nfs mounts breaks, then there should be an option to set
90          * the directory.
91          */
92         wapi_dir=getenv ("MONO_SHARED_DIR");
93         if(wapi_dir==NULL) {
94                 filename=g_build_filename (g_get_home_dir (), ".wapi", name,
95                                            NULL);
96         } else {
97                 filename=g_build_filename (wapi_dir, ".wapi", name, NULL);
98         }
99         g_free (name);
100
101         g_snprintf (file, _POSIX_PATH_MAX, "%s", filename);
102                 
103         /* No need to check if the dir already exists or check
104          * mkdir() errors, because on any error the open() call will
105          * report the problem.
106          */
107         dir=g_path_get_dirname (file);
108         mkdir (dir, 0755);
109         g_free (dir);
110         
111         return(file);
112 }
113
114 gpointer _wapi_shm_file_expand (gpointer mem, _wapi_shm_t type,
115                                 guint32 segment, guint32 old_len,
116                                 guint32 new_len)
117 {
118         int fd;
119         gpointer new_mem;
120         guchar *filename=_wapi_shm_file (type, segment);
121
122         if(old_len>=new_len) {
123                 return(mem);
124         }
125         
126         munmap (mem, old_len);
127         
128         fd=open (filename, O_RDWR, 0600);
129         if(fd==-1) {
130                 g_critical (G_GNUC_PRETTY_FUNCTION
131                             ": shared file [%s] open error: %s", filename,
132                             g_strerror (errno));
133                 return(NULL);
134         }
135
136         if(lseek (fd, new_len-1, SEEK_SET)==-1) {
137                 g_critical (G_GNUC_PRETTY_FUNCTION
138                             ": shared file [%s] lseek error: %s", filename,
139                             g_strerror (errno));
140                 return(NULL);
141         }
142         
143         if(write (fd, "", 1)==-1) {
144                 g_critical (G_GNUC_PRETTY_FUNCTION
145                             ": shared file [%s] write error: %s", filename,
146                             g_strerror (errno));
147                 return(NULL);
148         }
149         close (fd);
150         
151         new_mem=_wapi_shm_file_map (type, segment, NULL);
152         
153         return(new_mem);
154 }
155
156 static int _wapi_shm_file_open (const guchar *filename, _wapi_shm_t type,
157                                 gboolean *created)
158 {
159         int fd;
160         struct stat statbuf;
161         guint32 wanted_size;
162         
163         if(created) {
164                 *created=FALSE;
165         }
166         
167         if(type==WAPI_SHM_DATA) {
168                 wanted_size=sizeof(struct _WapiHandleShared_list);
169         } else if (type==WAPI_SHM_SCRATCH) {
170                 wanted_size=sizeof(struct _WapiHandleScratch);
171         } else {
172                 g_assert_not_reached ();
173         }
174         
175 try_again:
176         /* No O_CREAT yet, because we need to initialise the file if
177          * we have to create it.
178          */
179         fd=open (filename, O_RDWR, 0600);
180         if(fd==-1 && errno==ENOENT) {
181                 /* OK, its up to us to create it.  O_EXCL to avoid a
182                  * race condition where two processes can
183                  * simultaneously try and create the file
184                  */
185                 fd=open (filename, O_CREAT|O_EXCL|O_RDWR, 0600);
186                 if(fd==-1 && errno==EEXIST) {
187                         /* It's possible that the file was created in
188                          * between finding it didn't exist, and trying
189                          * to create it.  Just try opening it again
190                          */
191                         goto try_again;
192                 } else if (fd==-1) {
193                         g_critical (G_GNUC_PRETTY_FUNCTION
194                                     ": shared file [%s] open error: %s",
195                                     filename, g_strerror (errno));
196                         return(-1);
197                 } else {
198                         /* We created the file, so we need to expand
199                          * the file and inform the caller so it can
200                          * fork the handle daemon too.
201                          *
202                          * (wanted_size-1, because we're about to
203                          * write the other byte to actually expand the
204                          * file.)
205                          */
206                         if(lseek (fd, wanted_size-1, SEEK_SET)==-1) {
207                                 g_critical (G_GNUC_PRETTY_FUNCTION ": shared file [%s] lseek error: %s", filename, g_strerror (errno));
208                                 close (fd);
209                                 unlink (filename);
210                                 return(-1);
211                         }
212                         
213                         if(write (fd, "", 1)==-1) {
214                                 g_critical (G_GNUC_PRETTY_FUNCTION ": shared file [%s] write error: %s", filename, g_strerror (errno));
215                                 close (fd);
216                                 unlink (filename);
217                                 return(-1);
218                         }
219                         
220                         if(created) {
221                                 *created=TRUE;
222                         }
223
224                         /* The contents of the file is set to all
225                          * zero, because it is opened up with lseek,
226                          * so we don't need to do any more
227                          * initialisation here
228                          */
229                 }
230         } else if(fd==-1) {
231                 g_critical (G_GNUC_PRETTY_FUNCTION
232                             ": shared file [%s] open error: %s", filename,
233                             g_strerror (errno));
234                 return(-1);
235         }
236         
237         /* From now on, we need to delete the file before exiting on
238          * error if we created it (ie, if *created==TRUE)
239          */
240
241         /* Use stat to find the file size (instead of hard coding it)
242          * because we can expand the file later if needed (for more
243          * handles or scratch space.)
244          */
245         if(fstat (fd, &statbuf)==-1) {
246                 g_critical (G_GNUC_PRETTY_FUNCTION ": fstat error: %s",
247                             g_strerror (errno));
248                 if(*created==TRUE) {
249                         unlink (filename);
250                 }
251                 close (fd);
252                 return(-1);
253         }
254
255         if(statbuf.st_size < wanted_size) {
256 #ifdef HAVE_LARGE_FILE_SUPPORT
257                 /* Keep gcc quiet... */
258                 g_critical (G_GNUC_PRETTY_FUNCTION ": shared file [%s] is not big enough! (found %lld, need %d bytes)", filename, statbuf.st_size, wanted_size);
259 #else
260                 g_critical (G_GNUC_PRETTY_FUNCTION ": shared file [%s] is not big enough! (found %ld, need %d bytes)", filename, statbuf.st_size, wanted_size);
261 #endif
262                 if(*created==TRUE) {
263                         unlink (filename);
264                 }
265                 close (fd);
266                 return(-1);
267         }
268         
269         return(fd);
270 }
271
272 gpointer _wapi_shm_file_map (_wapi_shm_t type, guint32 segment,
273                              gboolean *created)
274 {
275         gpointer shm_seg;
276         int fd;
277         struct stat statbuf;
278         guchar *filename=_wapi_shm_file (type, segment);
279         
280         fd=_wapi_shm_file_open (filename, type, created);
281         if(fd==-1) {
282                 g_critical (G_GNUC_PRETTY_FUNCTION
283                             ": shared file [%s] open error", filename);
284                 return(NULL);
285         }
286         
287         if(fstat (fd, &statbuf)==-1) {
288                 g_critical (G_GNUC_PRETTY_FUNCTION ": fstat error: %s",
289                             g_strerror (errno));
290                 close (fd);
291                 return(NULL);
292         }
293
294         shm_seg=mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
295                       fd, 0);
296         if(shm_seg==MAP_FAILED) {
297                 g_critical (G_GNUC_PRETTY_FUNCTION ": mmap error: %s",
298                             g_strerror (errno));
299                 close (fd);
300                 return(NULL);
301         }
302                 
303         close (fd);
304         return(shm_seg);
305 }
306
307 /*
308  * _wapi_shm_attach:
309  * @success: Was it a success
310  *
311  * Attach to the shared memory file or create it if it did not
312  * exist. If it was created and daemon was FALSE a new daemon is
313  * forked into existence. Returns the memory area the file was mmapped
314  * to.
315  */
316 gboolean _wapi_shm_attach (struct _WapiHandleShared_list **data,
317                            struct _WapiHandleScratch **scratch)
318 {
319         gboolean data_created=FALSE, scratch_created=FALSE;
320         int tries;
321
322         *data=_wapi_shm_file_map (WAPI_SHM_DATA, 0, &data_created);
323         if(*data==NULL) {
324                 return(FALSE);
325         }
326         
327         *scratch=_wapi_shm_file_map (WAPI_SHM_SCRATCH, 0, &scratch_created);
328         if(*scratch==NULL) {
329                 if(data_created) {
330                         _wapi_shm_destroy ();
331                 }
332                 return(FALSE);
333         }
334         
335         if(data_created==TRUE) {
336 #ifdef VALGRINDING
337                 /* _wapi_daemon_main() does not return */
338                 _wapi_daemon_main (*data, *scratch);
339                         
340                 /* But just in case... */
341                 (*data)->daemon_running=DAEMON_DIED_AT_STARTUP;
342                 exit (-1);
343 #else
344                 pid_t pid;
345                         
346                 pid=fork ();
347                 if(pid==-1) {
348                         g_critical (G_GNUC_PRETTY_FUNCTION ": fork error: %s",
349                                     strerror (errno));
350                         _wapi_shm_destroy ();
351                         return(FALSE);
352                 } else if (pid==0) {
353                         int i;
354                         
355                         /* child */
356                         setsid ();
357                         
358                         /* FIXME: Set process title to something
359                          * informative
360                          */
361
362                         /* Start the daemon with a clean sheet of file
363                          * descriptors
364                          */
365                         for(i=3; i<getdtablesize (); i++) {
366                                 close (i);
367                         }
368                         
369                         /* _wapi_daemon_main() does not return */
370                         _wapi_daemon_main (*data, *scratch);
371                         
372                         /* But just in case... */
373                         (*data)->daemon_running=DAEMON_DIED_AT_STARTUP;
374                         exit (-1);
375                 }
376                 /* parent carries on */
377 #ifdef DEBUG
378                 g_message (G_GNUC_PRETTY_FUNCTION ": Daemon pid %d", pid);
379 #endif
380 #endif /* !VALGRINDING */
381         } else {
382                 /* Do some sanity checking on the shared memory we
383                  * attached
384                  */
385                 if(!((*data)->daemon_running==DAEMON_STARTING || 
386                      (*data)->daemon_running==DAEMON_RUNNING ||
387                      (*data)->daemon_running==DAEMON_DIED_AT_STARTUP) ||
388 #ifdef NEED_LINK_UNLINK
389                    (strncmp ((*data)->daemon, "/tmp/mono-handle-daemon-",
390                              24)!=0)) {
391 #else
392                    (strncmp ((*data)->daemon+1, "mono-handle-daemon-", 19)!=0)) {
393 #endif
394                         g_warning ("Shared memory sanity check failed.");
395                         return(FALSE);
396                 }
397         }
398                 
399         for(tries=0; (*data)->daemon_running==DAEMON_STARTING && tries < 100;
400             tries++) {
401                 /* wait for the daemon to sort itself out.  To be
402                  * completely safe, we should have a timeout before
403                  * giving up.
404                  */
405                 struct timespec sleepytime;
406                         
407                 sleepytime.tv_sec=0;
408                 sleepytime.tv_nsec=10000000;    /* 10ms */
409                         
410                 nanosleep (&sleepytime, NULL);
411         }
412         if(tries==100 && (*data)->daemon_running==DAEMON_STARTING) {
413                 /* Daemon didnt get going */
414                 if(data_created==TRUE) {
415                         _wapi_shm_destroy ();
416                 }
417                 g_warning ("The handle daemon didnt start up properly");
418                 return(FALSE);
419         }
420         
421         if((*data)->daemon_running==DAEMON_DIED_AT_STARTUP) {
422                 /* Oh dear, the daemon had an error starting up */
423                 if(data_created==TRUE) {
424                         _wapi_shm_destroy ();
425                 }
426                 g_warning ("Handle daemon failed to start");
427                 return(FALSE);
428         }
429                 
430         /* From now on, it's up to the daemon to delete the shared
431          * memory segment
432          */
433         
434         return(TRUE);
435 }
436
437 void _wapi_shm_destroy (void)
438 {
439 #ifndef DISABLE_SHARED_HANDLES
440 #ifdef DEBUG
441         g_message (G_GNUC_PRETTY_FUNCTION ": unlinking shared data");
442 #endif
443         /* Only delete the first segments.  The daemon will destroy
444          * any others when it exits
445          */
446         unlink (_wapi_shm_file (WAPI_SHM_DATA, 0));
447         unlink (_wapi_shm_file (WAPI_SHM_SCRATCH, 0));
448 #endif /* DISABLE_SHARED_HANDLES */
449 }
450