1 /* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 #include <semaphore.h>
34 #include <sys/statfs.h>
35 #include <linux_fsinfo.h>
36 #include "semaphoreP.h"
39 /* Compatibility defines. */
40 #define __endmntent endmntent
41 #define __fxstat64(vers, fd, buf) fstat64(fd, buf)
42 #define __getmntent_r getmntent_r
43 #define __setmntent setmntent
44 #define __statfs statfs
45 #define __libc_close close
46 #define __libc_open open
47 #define __libc_write write
49 /* Information about the mount point. */
50 struct mountpoint_info mountpoint attribute_hidden;
52 /* This is the default mount point. */
53 static const char defaultmount[] = "/dev/shm";
54 /* This is the default directory. */
55 static const char defaultdir[] = "/dev/shm/sem.";
57 /* Protect the `mountpoint' variable above. */
58 pthread_once_t __namedsem_once attribute_hidden = PTHREAD_ONCE_INIT;
61 /* Determine where the shmfs is mounted (if at all). */
64 __where_is_shmfs (void)
72 /* The canonical place is /dev/shm. This is at least what the
73 documentation tells everybody to do. */
74 if (__statfs (defaultmount, &f) == 0 && f.f_type == SHMFS_SUPER_MAGIC)
76 /* It is in the normal place. */
77 mountpoint.dir = (char *) defaultdir;
78 mountpoint.dirlen = sizeof (defaultdir) - 1;
83 /* OK, do it the hard way. Look through the /proc/mounts file and if
84 this does not exist through /etc/fstab to find the mount point. */
85 fp = __setmntent ("/proc/mounts", "r");
86 if (__builtin_expect (fp == NULL, 0))
88 fp = __setmntent (_PATH_MNTTAB, "r");
89 if (__builtin_expect (fp == NULL, 0))
90 /* There is nothing we can do. Blind guesses are not helpful. */
94 /* Now read the entries. */
95 while ((mp = __getmntent_r (fp, &resmem, buf, sizeof buf)) != NULL)
96 /* The original name is "shm" but this got changed in early Linux
98 if (strcmp (mp->mnt_type, "tmpfs") == 0
99 || strcmp (mp->mnt_type, "shm") == 0)
101 /* Found it. There might be more than one place where the
102 filesystem is mounted but one is enough for us. */
105 /* First make sure this really is the correct entry. At least
106 some versions of the kernel give wrong information because
107 of the implicit mount of the shmfs for SysV IPC. */
108 if (__statfs (mp->mnt_dir, &f) != 0 || f.f_type != SHMFS_SUPER_MAGIC)
111 namelen = strlen (mp->mnt_dir);
114 /* Hum, maybe some crippled entry. Keep on searching. */
117 mountpoint.dir = (char *) malloc (namelen + 4 + 2);
118 if (mountpoint.dir != NULL)
120 char *cp = mempcpy (mountpoint.dir, mp->mnt_dir, namelen);
123 cp = stpcpy (cp, "sem.");
124 mountpoint.dirlen = cp - mountpoint.dir;
130 /* Close the stream. */
135 /* Comparison function for search of existing mapping. */
138 __sem_search (const void *a, const void *b)
140 const struct inuse_sem *as = (const struct inuse_sem *) a;
141 const struct inuse_sem *bs = (const struct inuse_sem *) b;
143 if (as->ino != bs->ino)
144 /* Cannot return the difference the type is larger than int. */
145 return as->ino < bs->ino ? -1 : (as->ino == bs->ino ? 0 : 1);
147 if (as->dev != bs->dev)
148 /* Cannot return the difference the type is larger than int. */
149 return as->dev < bs->dev ? -1 : (as->dev == bs->dev ? 0 : 1);
151 return strcmp (as->name, bs->name);
155 /* The search tree for existing mappings. */
156 void *__sem_mappings attribute_hidden;
158 /* Lock to protect the search tree. */
159 lll_lock_t __sem_mappings_lock = LLL_LOCK_INITIALIZER;
162 /* Search for existing mapping and if possible add the one provided. */
164 check_add_mapping (const char *name, size_t namelen, int fd, sem_t *existing)
166 sem_t *result = SEM_FAILED;
168 /* Get the information about the file. */
169 #ifdef __UCLIBC_HAS_LFS__
171 if (__fxstat64 (_STAT_VER, fd, &st) == 0)
174 if (fstat (fd, &st) == 0)
178 lll_lock (__sem_mappings_lock);
180 /* Search for an existing mapping given the information we have. */
181 struct inuse_sem *fake;
182 fake = (struct inuse_sem *) alloca (sizeof (*fake) + namelen);
183 memcpy (fake->name, name, namelen);
184 fake->dev = st.st_dev;
185 fake->ino = st.st_ino;
187 struct inuse_sem **foundp = tfind (fake, &__sem_mappings, __sem_search);
190 /* There is already a mapping. Use it. */
191 result = (*foundp)->sem;
196 /* We haven't found a mapping. Install ione. */
197 struct inuse_sem *newp;
199 newp = (struct inuse_sem *) malloc (sizeof (*newp) + namelen);
202 /* If the caller hasn't provided any map it now. */
203 if (existing == SEM_FAILED)
204 existing = (sem_t *) mmap (NULL, sizeof (sem_t),
205 PROT_READ | PROT_WRITE, MAP_SHARED,
208 newp->dev = st.st_dev;
209 newp->ino = st.st_ino;
211 newp->sem = existing;
212 memcpy (newp->name, name, namelen);
214 /* Insert the new value. */
215 if (existing != MAP_FAILED
216 && tsearch (newp, &__sem_mappings, __sem_search) != NULL)
220 /* Something went wrong while inserting the new
221 value. We fail completely. */
226 /* Release the lock. */
227 lll_unlock (__sem_mappings_lock);
230 if (result != existing && existing != SEM_FAILED && existing != MAP_FAILED)
232 /* Do not disturb errno. */
233 INTERNAL_SYSCALL_DECL (err);
234 INTERNAL_SYSCALL (munmap, err, 2, existing, sizeof (sem_t));
242 sem_open (const char *name, int oflag, ...)
245 sem_t *result = SEM_FAILED;
248 /* Determine where the shmfs is mounted. */
249 __pthread_once (&__namedsem_once, __where_is_shmfs);
251 /* If we don't know the mount points there is nothing we can do. Ever. */
252 if (mountpoint.dir == NULL)
254 __set_errno (ENOSYS);
258 /* Construct the filename. */
259 while (name[0] == '/')
264 /* The name "/" is not supported. */
265 __set_errno (EINVAL);
268 size_t namelen = strlen (name) + 1;
270 /* Create the name of the final file. */
271 finalname = (char *) alloca (mountpoint.dirlen + namelen);
272 mempcpy (mempcpy (finalname, mountpoint.dir, mountpoint.dirlen),
275 /* If the semaphore object has to exist simply open it. */
276 if ((oflag & O_CREAT) == 0 || (oflag & O_EXCL) == 0)
279 fd = __libc_open (finalname,
280 (oflag & ~(O_CREAT|O_ACCMODE)) | O_NOFOLLOW | O_RDWR);
284 /* If we are supposed to create the file try this next. */
285 if ((oflag & O_CREAT) != 0 && errno == ENOENT)
288 /* Return. errno is already set. */
291 /* Check whether we already have this semaphore mapped and
292 create one if necessary. */
293 result = check_add_mapping (name, namelen, fd, SEM_FAILED);
297 /* We have to open a temporary file first since it must have the
298 correct form before we can start using it. */
305 va_start (ap, oflag);
307 mode = va_arg (ap, mode_t);
308 value = va_arg (ap, unsigned int);
312 if (value > SEM_VALUE_MAX)
314 __set_errno (EINVAL);
318 /* Create the initial file content. */
321 struct sem *iinitsem = (struct sem *) &initsem;
322 iinitsem->count = value;
324 /* Initialize the remaining bytes as well. */
325 memset ((char *) &initsem + sizeof (struct sem), '\0',
326 sizeof (sem_t) - sizeof (struct sem));
328 tmpfname = (char *) alloca (mountpoint.dirlen + 6 + 1);
329 char *xxxxxx = mempcpy (tmpfname, mountpoint.dir, mountpoint.dirlen);
335 /* Add the suffix for mktemp. */
336 strcpy (xxxxxx, "XXXXXX");
338 /* We really want to use mktemp here. We cannot use mkstemp
339 since the file must be opened with a specific mode. The
340 mode cannot later be set since then we cannot apply the
342 if (mktemp (tmpfname) == NULL)
345 /* Open the file. Make sure we do not overwrite anything. */
346 fd = __libc_open (tmpfname, O_RDWR | O_CREAT | O_EXCL, mode);
351 if (++retries < NRETRIES)
354 __set_errno (EAGAIN);
364 if (TEMP_FAILURE_RETRY (__libc_write (fd, &initsem, sizeof (sem_t)))
366 /* Map the sem_t structure from the file. */
367 && (result = (sem_t *) mmap (NULL, sizeof (sem_t),
368 PROT_READ | PROT_WRITE, MAP_SHARED,
369 fd, 0)) != MAP_FAILED)
371 /* Create the file. Don't overwrite an existing file. */
372 if (link (tmpfname, finalname) != 0)
374 /* Undo the mapping. */
375 (void) munmap (result, sizeof (sem_t));
377 /* Reinitialize 'result'. */
380 /* This failed. If O_EXCL is not set and the problem was
381 that the file exists, try again. */
382 if ((oflag & O_EXCL) == 0 && errno == EEXIST)
384 /* Remove the file. */
385 (void) unlink (tmpfname);
387 /* Close the file. */
388 (void) __libc_close (fd);
394 /* Insert the mapping into the search tree. This also
395 determines whether another thread sneaked by and already
396 added such a mapping despite the fact that we created it. */
397 result = check_add_mapping (name, namelen, fd, result);
400 /* Now remove the temporary name. This should never fail. If
401 it fails we leak a file name. Better fix the kernel. */
402 (void) unlink (tmpfname);
405 /* Map the mmap error to the error we need. */
406 if (MAP_FAILED != (void *) SEM_FAILED && result == MAP_FAILED)
409 /* We don't need the file descriptor anymore. */
412 /* Do not disturb errno. */
413 INTERNAL_SYSCALL_DECL (err);
414 INTERNAL_SYSCALL (close, err, 1, fd);