2 * shared.c: Shared memory handling, and daemon launching
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
17 #include <sys/types.h>
23 #include <sys/utsname.h>
25 #include <mono/io-layer/wapi.h>
26 #include <mono/io-layer/wapi-private.h>
27 #include <mono/io-layer/shared.h>
28 #include <mono/io-layer/handles-private.h>
32 static guchar *_wapi_shm_file (_wapi_shm_t type)
34 static guchar file[_POSIX_PATH_MAX];
35 guchar *name = NULL, *filename, *dir, *wapi_dir;
36 gchar machine_name[256];
42 ubuf.machine[0] = '\0';
43 ubuf.sysname[0] = '\0';
46 if (gethostname(machine_name, sizeof(machine_name)) != 0)
47 machine_name[0] = '\0';
51 name = g_strdup_printf ("shared_data-%s-%s-%s-%d-%d-%d",
52 machine_name, ubuf.sysname,
54 sizeof(struct _WapiHandleShared),
55 _WAPI_HANDLE_VERSION, 0);
58 case WAPI_SHM_FILESHARE:
59 name = g_strdup_printf ("shared_fileshare-%s-%s-%s-%d-%d-%d",
60 machine_name, ubuf.sysname,
62 sizeof(struct _WapiFileShare),
63 _WAPI_HANDLE_VERSION, 0);
67 /* I don't know how nfs affects mmap. If mmap() of files on
68 * nfs mounts breaks, then there should be an option to set
71 wapi_dir = getenv ("MONO_SHARED_DIR");
72 if (wapi_dir == NULL) {
73 filename = g_build_filename (g_get_home_dir (), ".wapi", name,
76 filename = g_build_filename (wapi_dir, ".wapi", name, NULL);
80 g_snprintf (file, _POSIX_PATH_MAX, "%s", filename);
83 /* No need to check if the dir already exists or check
84 * mkdir() errors, because on any error the open() call will
87 dir = g_path_get_dirname (file);
94 static int _wapi_shm_file_open (const guchar *filename, guint32 wanted_size)
99 gboolean created = FALSE;
102 /* No O_CREAT yet, because we need to initialise the file if
103 * we have to create it.
105 fd = open (filename, O_RDWR, 0600);
106 if (fd == -1 && errno == ENOENT) {
107 /* OK, its up to us to create it. O_EXCL to avoid a
108 * race condition where two processes can
109 * simultaneously try and create the file
111 fd = open (filename, O_CREAT|O_EXCL|O_RDWR, 0600);
112 if (fd == -1 && errno == EEXIST) {
113 /* It's possible that the file was created in
114 * between finding it didn't exist, and trying
115 * to create it. Just try opening it again
118 } else if (fd == -1) {
119 g_critical ("%s: shared file [%s] open error: %s",
120 __func__, filename, g_strerror (errno));
123 /* We created the file, so we need to expand
126 * (wanted_size-1, because we're about to
127 * write the other byte to actually expand the
130 if (lseek (fd, wanted_size-1, SEEK_SET) == -1) {
131 g_critical ("%s: shared file [%s] lseek error: %s", __func__, filename, g_strerror (errno));
138 ret = write (fd, "", 1);
139 } while (ret == -1 && errno == EINTR);
142 g_critical ("%s: shared file [%s] write error: %s", __func__, filename, g_strerror (errno));
150 /* The contents of the file is set to all
151 * zero, because it is opened up with lseek,
152 * so we don't need to do any more
153 * initialisation here
156 } else if (fd == -1) {
157 g_critical ("%s: shared file [%s] open error: %s", __func__,
158 filename, g_strerror (errno));
162 /* Use stat to find the file size (instead of hard coding it)
163 * because we can expand the file later if needed (for more
164 * handles or scratch space.)
166 if (fstat (fd, &statbuf) == -1) {
167 g_critical ("%s: fstat error: %s", __func__,
169 if (created == TRUE) {
176 if (statbuf.st_size < wanted_size) {
178 if (created == TRUE) {
179 #ifdef HAVE_LARGE_FILE_SUPPORT
180 /* Keep gcc quiet... */
181 g_critical ("%s: shared file [%s] is not big enough! (found %lld, need %d bytes)", __func__, filename, statbuf.st_size, wanted_size);
183 g_critical ("%s: shared file [%s] is not big enough! (found %ld, need %d bytes)", __func__, filename, statbuf.st_size, wanted_size);
188 /* We didn't create it, so just try opening it again */
198 * @success: Was it a success
200 * Attach to the shared memory file or create it if it did not exist.
201 * Returns the memory area the file was mmapped to.
203 gpointer _wapi_shm_attach (_wapi_shm_t type)
208 guchar *filename=_wapi_shm_file (type);
213 size = sizeof(struct _WapiHandleSharedLayout);
216 case WAPI_SHM_FILESHARE:
217 size = sizeof(struct _WapiFileShareLayout);
221 fd = _wapi_shm_file_open (filename, size);
223 g_critical ("%s: shared file [%s] open error", __func__,
228 if (fstat (fd, &statbuf)==-1) {
229 g_critical ("%s: fstat error: %s", __func__,
235 shm_seg = mmap (NULL, statbuf.st_size, PROT_READ|PROT_WRITE,
237 if (shm_seg == MAP_FAILED) {
238 g_critical ("%s: mmap error: %s", __func__,
248 void _wapi_shm_semaphores_init ()
250 key_t key = ftok (_wapi_shm_file (WAPI_SHM_DATA), 'M');
253 /* Yet more barmy API - this union is a well-defined parameter
254 * in a syscall, yet I still have to define it here as it
255 * doesn't appear in a header
259 struct semid_ds *buf;
262 ushort def_vals[_WAPI_SHARED_SEM_COUNT];
266 for (i = 0; i < _WAPI_SHARED_SEM_COUNT; i++) {
269 defs.array = def_vals;
273 oldkey = _wapi_shared_layout->sem_key;
277 g_message ("%s: Creating with new key (0x%x)", __func__, key);
280 /* The while loop attempts to make some sense of the
281 * bonkers 'think of a random number' method of
282 * picking a key without collision with other
285 while ((_wapi_sem_id = semget (key, _WAPI_SHARED_SEM_COUNT,
286 IPC_CREAT | IPC_EXCL | 0600)) == -1) {
287 if (errno == ENOMEM) {
288 g_critical ("%s: semget error: %s", __func__,
290 } else if (errno == ENOSPC) {
291 g_critical ("%s: semget error: %s. Try deleting some semaphores with ipcs and ipcrm", __func__, g_strerror (errno));
292 } else if (errno != EEXIST) {
294 g_warning ("%s: semget error: %s key 0x%x - trying again", __func__,
295 g_strerror (errno), key);
300 g_message ("%s: Got (%s), trying with new key (0x%x)",
301 __func__, g_strerror (errno), key);
304 /* Got a semaphore array, so initialise it and install
305 * the key into the shared memory
308 if (semctl (_wapi_sem_id, 0, SETALL, defs) == -1) {
310 g_warning ("%s: semctl init error: %s - trying again", __func__, g_strerror (errno));
312 /* Something went horribly wrong, so try
313 * getting a new set from scratch
315 semctl (_wapi_sem_id, 0, IPC_RMID);
319 if (InterlockedCompareExchange (&_wapi_shared_layout->sem_key,
321 /* Someone else created one and installed the
322 * key while we were working, so delete the
323 * array we created and fall through to the
324 * 'key already known' case.
326 semctl (_wapi_sem_id, 0, IPC_RMID);
327 oldkey = _wapi_shared_layout->sem_key;
329 /* We've installed this semaphore set's key into
337 g_message ("%s: Trying with old key 0x%x", __func__, oldkey);
340 _wapi_sem_id = semget (oldkey, _WAPI_SHARED_SEM_COUNT, 0600);
341 if (_wapi_sem_id == -1) {
343 g_warning ("%s: semget error opening old key 0x%x (%s) - trying again",
344 __func__, oldkey,g_strerror (errno));
346 /* Someone must have deleted the semaphore set, so
347 * blow away the bad key and try again
349 InterlockedCompareExchange (&_wapi_shared_layout->sem_key, 0,
356 int _wapi_shm_sem_lock (int sem)
362 g_message ("%s: locking sem %d", __func__, sem);
367 ops.sem_flg = SEM_UNDO;
370 ret = semop (_wapi_sem_id, &ops, 1);
371 } while (ret == -1 && errno == EINTR);
374 /* Turn this into a pthreads-style return value */
379 g_message ("%s: returning %d (%s)", __func__, ret, g_strerror (ret));
385 int _wapi_shm_sem_trylock (int sem)
391 g_message ("%s: trying to lock sem %d", __func__, sem);
396 ops.sem_flg = IPC_NOWAIT | SEM_UNDO;
399 ret = semop (_wapi_sem_id, &ops, 1);
400 } while (ret == -1 && errno == EINTR);
403 /* Turn this into a pthreads-style return value */
408 /* But pthreads uses this code instead */
413 g_message ("%s: returning %d (%s)", __func__, ret, g_strerror (ret));
419 int _wapi_shm_sem_unlock (int sem)
425 g_message ("%s: unlocking sem %d", __func__, sem);
430 ops.sem_flg = SEM_UNDO;
433 ret = semop (_wapi_sem_id, &ops, 1);
434 } while (ret == -1 && errno == EINTR);
437 /* Turn this into a pthreads-style return value */
442 g_message ("%s: returning %d (%s)", __func__, ret, g_strerror (ret));