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 static guchar *shared_file (void)
66 static guchar *file=NULL;
73 /* Change the filename whenever the format of the contents
76 name=g_strdup_printf ("shared_data-%d", _WAPI_HANDLE_VERSION);
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
82 file=g_build_filename (g_get_home_dir (), ".wapi", name, NULL);
85 /* No need to check if the dir already exists or check
86 * mkdir() errors, because on any error the open() call will
89 dir=g_path_get_dirname (file);
98 * @success: Was it a success
100 * Attach to the shared memory file or create it if it did not
101 * exist. If it was created and daemon was FALSE a new daemon is
102 * forked into existence. Returns the memory area the file was mmapped
105 gpointer _wapi_shm_attach (gboolean *success)
109 gboolean fork_daemon=FALSE;
111 struct _WapiHandleShared_list *data;
113 int wanted_size=sizeof(struct _WapiHandleShared_list) +
114 _WAPI_SHM_SCRATCH_SIZE;
117 /* No O_CREAT yet, because we need to initialise the file if
118 * we have to create it.
120 fd=open (shared_file (), O_RDWR, 0600);
121 if(fd==-1 && errno==ENOENT) {
122 /* OK, its up to us to create it. O_EXCL to avoid a
123 * race condition where two processes can
124 * simultaneously try and create the file
126 fd=open (shared_file (), O_CREAT|O_EXCL|O_RDWR, 0600);
127 if(fd==-1 && errno==EEXIST) {
128 /* It's possible that the file was created in
129 * between finding it didn't exist, and trying
130 * to create it. Just try opening it again
134 g_critical (G_GNUC_PRETTY_FUNCTION
135 ": shared file [%s] open error: %s",
136 shared_file (), g_strerror (errno));
139 /* We created the file, so we need to expand
140 * the file and fork the handle daemon too
142 if(lseek (fd, wanted_size, SEEK_SET)==-1) {
143 g_critical (G_GNUC_PRETTY_FUNCTION ": shared file [%s] lseek error: %s", shared_file (), g_strerror (errno));
144 _wapi_shm_destroy ();
148 if(write (fd, "", 1)==-1) {
149 g_critical (G_GNUC_PRETTY_FUNCTION ": shared file [%s] write error: %s", shared_file (), g_strerror (errno));
150 _wapi_shm_destroy ();
156 /* The contents of the file is set to all
157 * zero, because it is opened up with lseek,
158 * so we don't need to do any more
159 * initialisation here
163 g_critical (G_GNUC_PRETTY_FUNCTION
164 ": shared file [%s] open error: %s",
165 shared_file (), g_strerror (errno));
168 /* We dont need to fork the handle daemon */
171 /* From now on, we need to delete the file before exiting on
172 * error if we created it (ie, if fork_daemon==TRUE)
175 /* Use stat to find the file size (instead of hard coding it)
176 * so that we can expand the file later if needed (for more
177 * handles or scratch space, though that will require a tweak
178 * to the file format to store the count).
180 if(fstat (fd, &statbuf)==-1) {
181 g_critical (G_GNUC_PRETTY_FUNCTION ": fstat error: %s",
183 if(fork_daemon==TRUE) {
184 _wapi_shm_destroy ();
189 if(statbuf.st_size < wanted_size) {
190 #ifdef HAVE_LARGE_FILE_SUPPORT
191 /* Keep gcc quiet... */
192 g_critical (G_GNUC_PRETTY_FUNCTION ": shared file [%s] is not big enough! (found %lld, need %d bytes)", shared_file (), statbuf.st_size, wanted_size);
194 g_critical (G_GNUC_PRETTY_FUNCTION ": shared file [%s] is not big enough! (found %ld, need %d bytes)", shared_file (), statbuf.st_size, wanted_size);
196 if(fork_daemon==TRUE) {
197 _wapi_shm_destroy ();
202 shm_seg=mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
204 if(shm_seg==MAP_FAILED) {
205 g_critical (G_GNUC_PRETTY_FUNCTION ": mmap error: %s",
207 if(fork_daemon==TRUE) {
208 _wapi_shm_destroy ();
216 if(fork_daemon==TRUE) {
221 g_critical (G_GNUC_PRETTY_FUNCTION ": fork error: %s",
223 _wapi_shm_destroy ();
231 /* FIXME: Clean up memory. We can delete all
234 /* FIXME2: Set process title to something
238 /* Start the daemon with a clean sheet of file
241 for(i=3; i<getdtablesize (); i++) {
245 /* _wapi_daemon_main() does not return */
246 _wapi_daemon_main (data);
248 /* But just in case... */
249 data->daemon_running=DAEMON_DIED_AT_STARTUP;
252 /* parent carries on */
254 g_message (G_GNUC_PRETTY_FUNCTION ": Daemon pid %d", pid);
257 /* Do some sanity checking on the shared memory we
260 if(!(data->daemon_running==DAEMON_STARTING ||
261 data->daemon_running==DAEMON_RUNNING ||
262 data->daemon_running==DAEMON_DIED_AT_STARTUP) ||
263 #ifdef NEED_LINK_UNLINK
264 (strncmp (data->daemon, "/tmp/mono-handle-daemon-",
267 (strncmp (data->daemon+1, "mono-handle-daemon-", 19)!=0)) {
269 g_warning ("Shared memory sanity check failed.");
275 for(tries=0; data->daemon_running==DAEMON_STARTING && tries < 100;
277 /* wait for the daemon to sort itself out. To be
278 * completely safe, we should have a timeout before
281 struct timespec sleepytime;
284 sleepytime.tv_nsec=10000000; /* 10ms */
286 nanosleep (&sleepytime, NULL);
288 if(tries==100 && data->daemon_running==DAEMON_STARTING) {
289 /* Daemon didnt get going */
290 if(fork_daemon==TRUE) {
291 _wapi_shm_destroy ();
293 g_warning ("The handle daemon didnt start up properly");
298 if(data->daemon_running==DAEMON_DIED_AT_STARTUP) {
299 /* Oh dear, the daemon had an error starting up */
300 if(fork_daemon==TRUE) {
301 _wapi_shm_destroy ();
303 g_warning ("Handle daemon failed to start");
308 /* From now on, it's up to the daemon to delete the shared
316 void _wapi_shm_destroy (void)
318 #ifndef DISABLE_SHARED_HANDLES
320 g_message (G_GNUC_PRETTY_FUNCTION ": unlinking %s", shared_file ());
322 unlink (shared_file ());
323 #endif /* DISABLE_SHARED_HANDLES */