2 * shared.c: Shared memory handling, and daemon launching
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
11 * Code to support inter-process sharing of handles.
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
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.
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.
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.
52 #include <sys/types.h>
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>
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.)
70 guchar *_wapi_shm_file (_wapi_shm_t type, guint32 segment)
72 static guchar file[_POSIX_PATH_MAX];
73 guchar *name = NULL, *filename, *dir, *wapi_dir;
74 gchar machine_name[256];
76 if (gethostname(machine_name, sizeof(machine_name)) != 0)
77 machine_name[0] = '\0';
79 /* Change the filename whenever the format of the contents
82 if(type==WAPI_SHM_DATA) {
83 name=g_strdup_printf ("shared_data-%s-%d-%d",
84 machine_name, _WAPI_HANDLE_VERSION, segment);
85 } else if (type==WAPI_SHM_SCRATCH) {
86 name=g_strdup_printf ("shared_scratch-%s-%d-%d",
87 machine_name, _WAPI_HANDLE_VERSION, segment);
89 g_assert_not_reached ();
92 /* I don't know how nfs affects mmap. If mmap() of files on
93 * nfs mounts breaks, then there should be an option to set
96 wapi_dir=getenv ("MONO_SHARED_DIR");
98 filename=g_build_filename (g_get_home_dir (), ".wapi", name,
101 filename=g_build_filename (wapi_dir, ".wapi", name, NULL);
105 g_snprintf (file, _POSIX_PATH_MAX, "%s", filename);
108 /* No need to check if the dir already exists or check
109 * mkdir() errors, because on any error the open() call will
110 * report the problem.
112 dir=g_path_get_dirname (file);
119 gpointer _wapi_shm_file_expand (gpointer mem, _wapi_shm_t type,
120 guint32 segment, guint32 old_len,
125 guchar *filename=_wapi_shm_file (type, segment);
128 if(old_len>=new_len) {
132 munmap (mem, old_len);
134 fd=open (filename, O_RDWR, 0600);
136 g_critical (G_GNUC_PRETTY_FUNCTION
137 ": shared file [%s] open error: %s", filename,
142 if(lseek (fd, new_len-1, SEEK_SET)==-1) {
143 g_critical (G_GNUC_PRETTY_FUNCTION
144 ": shared file [%s] lseek error: %s", filename,
150 ret=write (fd, "", 1);
152 while (ret==-1 && errno==EINTR);
155 g_critical (G_GNUC_PRETTY_FUNCTION
156 ": shared file [%s] write error: %s", filename,
163 new_mem=_wapi_shm_file_map (type, segment, NULL, NULL);
168 static int _wapi_shm_file_open (const guchar *filename, _wapi_shm_t type,
173 guint32 wanted_size = 0;
180 if(type==WAPI_SHM_DATA) {
181 wanted_size=sizeof(struct _WapiHandleShared_list);
182 } else if (type==WAPI_SHM_SCRATCH) {
183 wanted_size=sizeof(struct _WapiHandleScratch) +
184 (_WAPI_SHM_SCRATCH_SIZE - MONO_ZERO_ARRAY_LENGTH);
186 g_assert_not_reached ();
190 /* No O_CREAT yet, because we need to initialise the file if
191 * we have to create it.
193 fd=open (filename, O_RDWR, 0600);
194 if(fd==-1 && errno==ENOENT) {
195 /* OK, its up to us to create it. O_EXCL to avoid a
196 * race condition where two processes can
197 * simultaneously try and create the file
199 fd=open (filename, O_CREAT|O_EXCL|O_RDWR, 0600);
200 if(fd==-1 && errno==EEXIST) {
201 /* It's possible that the file was created in
202 * between finding it didn't exist, and trying
203 * to create it. Just try opening it again
207 g_critical (G_GNUC_PRETTY_FUNCTION
208 ": shared file [%s] open error: %s",
209 filename, g_strerror (errno));
212 /* We created the file, so we need to expand
213 * the file and inform the caller so it can
214 * fork the handle daemon too.
216 * (wanted_size-1, because we're about to
217 * write the other byte to actually expand the
220 if(lseek (fd, wanted_size-1, SEEK_SET)==-1) {
221 g_critical (G_GNUC_PRETTY_FUNCTION ": shared file [%s] lseek error: %s", filename, g_strerror (errno));
228 ret=write (fd, "", 1);
230 while (ret==-1 && errno==EINTR);
233 g_critical (G_GNUC_PRETTY_FUNCTION ": shared file [%s] write error: %s", filename, g_strerror (errno));
243 /* The contents of the file is set to all
244 * zero, because it is opened up with lseek,
245 * so we don't need to do any more
246 * initialisation here
250 g_critical (G_GNUC_PRETTY_FUNCTION
251 ": shared file [%s] open error: %s", filename,
256 /* From now on, we need to delete the file before exiting on
257 * error if we created it (ie, if *created==TRUE)
260 /* Use stat to find the file size (instead of hard coding it)
261 * because we can expand the file later if needed (for more
262 * handles or scratch space.)
264 if(fstat (fd, &statbuf)==-1) {
265 g_critical (G_GNUC_PRETTY_FUNCTION ": fstat error: %s",
267 if(created && *created==TRUE) {
274 if(statbuf.st_size < wanted_size) {
276 if(created && *created==TRUE) {
277 #ifdef HAVE_LARGE_FILE_SUPPORT
278 /* Keep gcc quiet... */
279 g_critical (G_GNUC_PRETTY_FUNCTION ": shared file [%s] is not big enough! (found %lld, need %d bytes)", filename, statbuf.st_size, wanted_size);
281 g_critical (G_GNUC_PRETTY_FUNCTION ": shared file [%s] is not big enough! (found %ld, need %d bytes)", filename, statbuf.st_size, wanted_size);
286 /* We didn't create it, so just try opening it again */
294 gpointer _wapi_shm_file_map (_wapi_shm_t type, guint32 segment,
295 gboolean *created, off_t *size)
300 guchar *filename=_wapi_shm_file (type, segment);
302 fd=_wapi_shm_file_open (filename, type, created);
304 g_critical (G_GNUC_PRETTY_FUNCTION
305 ": shared file [%s] open error", filename);
309 if(fstat (fd, &statbuf)==-1) {
310 g_critical (G_GNUC_PRETTY_FUNCTION ": fstat error: %s",
316 *size=statbuf.st_size;
319 shm_seg=mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
321 if(shm_seg==MAP_FAILED) {
322 g_critical (G_GNUC_PRETTY_FUNCTION ": mmap error: %s",
334 * @success: Was it a success
336 * Attach to the shared memory file or create it if it did not
337 * exist. If it was created and daemon was FALSE a new daemon is
338 * forked into existence. Returns the memory area the file was mmapped
341 gboolean _wapi_shm_attach (struct _WapiHandleShared_list **data,
342 struct _WapiHandleScratch **scratch)
344 gboolean data_created=FALSE, scratch_created=FALSE;
345 off_t data_size, scratch_size;
346 int tries, closing_tries=0;
349 *data=_wapi_shm_file_map (WAPI_SHM_DATA, 0, &data_created, &data_size);
354 *scratch=_wapi_shm_file_map (WAPI_SHM_SCRATCH, 0, &scratch_created,
358 _wapi_shm_destroy ();
364 (*scratch)->data_len = scratch_size -
365 (sizeof(struct _WapiHandleScratch) - MONO_ZERO_ARRAY_LENGTH);
367 if(data_created==FALSE && (*data)->daemon_running==DAEMON_CLOSING) {
368 /* Daemon is closing down, give it a few ms and try
372 struct timespec sleepytime;
374 /* Something must have gone wrong, so delete the
375 * shared segments and try again.
377 _wapi_shm_destroy ();
379 munmap (*data, data_size);
380 munmap (*scratch, scratch_size);
382 if(closing_tries++ == 5) {
383 /* Still can't get going, so bail out */
384 g_warning ("The handle daemon is stuck closing");
389 sleepytime.tv_nsec=10000000; /* 10ms */
391 nanosleep (&sleepytime, NULL);
395 if(data_created==TRUE) {
397 /* _wapi_daemon_main() does not return */
398 _wapi_daemon_main (*data, *scratch);
400 /* But just in case... */
401 (*data)->daemon_running=DAEMON_DIED_AT_STARTUP;
408 g_critical (G_GNUC_PRETTY_FUNCTION ": fork error: %s",
410 _wapi_shm_destroy ();
418 /* FIXME: Set process title to something
422 /* Start the daemon with a clean sheet of file
425 for(i=3; i<getdtablesize (); i++) {
429 /* _wapi_daemon_main() does not return */
430 _wapi_daemon_main (*data, *scratch);
432 /* But just in case... */
433 (*data)->daemon_running=DAEMON_DIED_AT_STARTUP;
436 /* parent carries on */
438 g_message (G_GNUC_PRETTY_FUNCTION ": Daemon pid %d", pid);
440 #endif /* !VALGRINDING */
443 for(tries=0; (*data)->daemon_running==DAEMON_STARTING && tries < 100;
445 /* wait for the daemon to sort itself out. To be
446 * completely safe, we should have a timeout before
449 struct timespec sleepytime;
452 sleepytime.tv_nsec=10000000; /* 10ms */
454 nanosleep (&sleepytime, NULL);
456 if(tries==100 && (*data)->daemon_running==DAEMON_STARTING) {
457 /* Daemon didnt get going */
458 struct timespec sleepytime;
460 /* Something must have gone wrong, so delete the
461 * shared segments and try again.
463 _wapi_shm_destroy ();
465 /* Daemon didn't get going, give it a few ms and try
469 munmap (*data, data_size);
470 munmap (*scratch, scratch_size);
472 if(closing_tries++ == 5) {
473 /* Still can't get going, so bail out */
474 g_warning ("The handle daemon didnt start up properly");
479 sleepytime.tv_nsec=10000000; /* 10ms */
481 nanosleep (&sleepytime, NULL);
485 if((*data)->daemon_running==DAEMON_DIED_AT_STARTUP) {
486 /* Oh dear, the daemon had an error starting up */
487 if(data_created==TRUE) {
488 _wapi_shm_destroy ();
490 g_warning ("Handle daemon failed to start");
494 /* Do some sanity checking on the shared memory we
497 if(((*data)->daemon_running!=DAEMON_RUNNING) ||
498 #ifdef NEED_LINK_UNLINK
499 (strncmp ((*data)->daemon, "/tmp/mono-handle-daemon-",
502 (strncmp ((*data)->daemon+1, "mono-handle-daemon-", 19)!=0)) {
504 g_warning ("Shared memory sanity check failed.");
505 g_warning("status: %d", (*data)->daemon_running);
506 #ifdef NEED_LINK_UNLINK
507 g_warning("daemon: [%s]", (*data)->daemon);
509 g_warning("daemon: [%s]", (*data)->daemon+1);
514 /* From now on, it's up to the daemon to delete the shared
521 void _wapi_shm_destroy (void)
523 #ifndef DISABLE_SHARED_HANDLES
525 g_message (G_GNUC_PRETTY_FUNCTION ": unlinking shared data");
527 /* Only delete the first segments. The daemon will destroy
528 * any others when it exits
530 unlink (_wapi_shm_file (WAPI_SHM_DATA, 0));
531 unlink (_wapi_shm_file (WAPI_SHM_SCRATCH, 0));
532 #endif /* DISABLE_SHARED_HANDLES */