1 /* Copyright (C) 2002, 2003, 2006, 2007, 2009 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"
37 #include "../../libc/misc/internals/tempname.h"
40 /* Compatibility defines. */
41 #define __endmntent endmntent
42 #define __fxstat64(vers, fd, buf) fstat64(fd, buf)
43 #define __getmntent_r getmntent_r
44 #define __setmntent setmntent
45 #define __statfs statfs
46 #define __libc_close close
47 #define __libc_open open
48 #define __libc_write write
51 /* Information about the mount point. */
52 struct mountpoint_info mountpoint attribute_hidden;
54 /* This is the default mount point. */
55 static const char defaultmount[] = "/dev/shm";
56 /* This is the default directory. */
57 static const char defaultdir[] = "/dev/shm/sem.";
59 /* Protect the `mountpoint' variable above. */
60 pthread_once_t __namedsem_once attribute_hidden = PTHREAD_ONCE_INIT;
63 /* Determine where the shmfs is mounted (if at all). */
66 __where_is_shmfs (void)
74 /* The canonical place is /dev/shm. This is at least what the
75 documentation tells everybody to do. */
76 if (__statfs (defaultmount, &f) == 0 && f.f_type == SHMFS_SUPER_MAGIC)
78 /* It is in the normal place. */
79 mountpoint.dir = (char *) defaultdir;
80 mountpoint.dirlen = sizeof (defaultdir) - 1;
85 /* OK, do it the hard way. Look through the /proc/mounts file and if
86 this does not exist through /etc/fstab to find the mount point. */
87 fp = __setmntent ("/proc/mounts", "r");
88 if (__builtin_expect (fp == NULL, 0))
90 fp = __setmntent (_PATH_MNTTAB, "r");
91 if (__builtin_expect (fp == NULL, 0))
92 /* There is nothing we can do. Blind guesses are not helpful. */
96 /* Now read the entries. */
97 while ((mp = __getmntent_r (fp, &resmem, buf, sizeof buf)) != NULL)
98 /* The original name is "shm" but this got changed in early Linux
100 if (strcmp (mp->mnt_type, "tmpfs") == 0
101 || strcmp (mp->mnt_type, "shm") == 0)
103 /* Found it. There might be more than one place where the
104 filesystem is mounted but one is enough for us. */
107 /* First make sure this really is the correct entry. At least
108 some versions of the kernel give wrong information because
109 of the implicit mount of the shmfs for SysV IPC. */
110 if (__statfs (mp->mnt_dir, &f) != 0 || f.f_type != SHMFS_SUPER_MAGIC)
113 namelen = strlen (mp->mnt_dir);
116 /* Hum, maybe some crippled entry. Keep on searching. */
119 mountpoint.dir = (char *) malloc (namelen + 4 + 2);
120 if (mountpoint.dir != NULL)
122 char *cp = mempcpy (mountpoint.dir, mp->mnt_dir, namelen);
125 cp = stpcpy (cp, "sem.");
126 mountpoint.dirlen = cp - mountpoint.dir;
132 /* Close the stream. */
137 /* Comparison function for search of existing mapping. */
140 __sem_search (const void *a, const void *b)
142 const struct inuse_sem *as = (const struct inuse_sem *) a;
143 const struct inuse_sem *bs = (const struct inuse_sem *) b;
145 if (as->ino != bs->ino)
146 /* Cannot return the difference the type is larger than int. */
147 return as->ino < bs->ino ? -1 : (as->ino == bs->ino ? 0 : 1);
149 if (as->dev != bs->dev)
150 /* Cannot return the difference the type is larger than int. */
151 return as->dev < bs->dev ? -1 : (as->dev == bs->dev ? 0 : 1);
153 return strcmp (as->name, bs->name);
157 /* The search tree for existing mappings. */
158 void *__sem_mappings attribute_hidden;
160 /* Lock to protect the search tree. */
161 int __sem_mappings_lock attribute_hidden = LLL_LOCK_INITIALIZER;
164 /* Search for existing mapping and if possible add the one provided. */
166 check_add_mapping (const char *name, size_t namelen, int fd, sem_t *existing)
168 sem_t *result = SEM_FAILED;
170 /* Get the information about the file. */
171 #ifdef __UCLIBC_HAS_LFS__
173 if (__fxstat64 (_STAT_VER, fd, &st) == 0)
176 if (fstat (fd, &st) == 0)
180 lll_lock (__sem_mappings_lock, LLL_PRIVATE);
182 /* Search for an existing mapping given the information we have. */
183 struct inuse_sem *fake;
184 fake = (struct inuse_sem *) alloca (sizeof (*fake) + namelen);
185 memcpy (fake->name, name, namelen);
186 fake->dev = st.st_dev;
187 fake->ino = st.st_ino;
189 struct inuse_sem **foundp = tfind (fake, &__sem_mappings, __sem_search);
192 /* There is already a mapping. Use it. */
193 result = (*foundp)->sem;
198 /* We haven't found a mapping. Install ione. */
199 struct inuse_sem *newp;
201 newp = (struct inuse_sem *) malloc (sizeof (*newp) + namelen);
204 /* If the caller hasn't provided any map it now. */
205 if (existing == SEM_FAILED)
206 existing = (sem_t *) mmap (NULL, sizeof (sem_t),
207 PROT_READ | PROT_WRITE, MAP_SHARED,
210 newp->dev = st.st_dev;
211 newp->ino = st.st_ino;
213 newp->sem = existing;
214 memcpy (newp->name, name, namelen);
216 /* Insert the new value. */
217 if (existing != MAP_FAILED
218 && tsearch (newp, &__sem_mappings, __sem_search) != NULL)
222 /* Something went wrong while inserting the new
223 value. We fail completely. */
228 /* Release the lock. */
229 lll_unlock (__sem_mappings_lock, LLL_PRIVATE);
232 if (result != existing && existing != SEM_FAILED && existing != MAP_FAILED)
234 /* Do not disturb errno. */
235 INTERNAL_SYSCALL_DECL (err);
236 INTERNAL_SYSCALL (munmap, err, 2, existing, sizeof (sem_t));
244 sem_open (const char *name, int oflag, ...)
247 sem_t *result = SEM_FAILED;
250 /* Determine where the shmfs is mounted. */
251 INTUSE(__pthread_once) (&__namedsem_once, __where_is_shmfs);
253 /* If we don't know the mount points there is nothing we can do. Ever. */
254 if (mountpoint.dir == NULL)
256 __set_errno (ENOSYS);
260 /* Construct the filename. */
261 while (name[0] == '/')
266 /* The name "/" is not supported. */
267 __set_errno (EINVAL);
270 size_t namelen = strlen (name) + 1;
272 /* Create the name of the final file. */
273 finalname = (char *) alloca (mountpoint.dirlen + namelen);
274 mempcpy (mempcpy (finalname, mountpoint.dir, mountpoint.dirlen),
277 /* If the semaphore object has to exist simply open it. */
278 if ((oflag & O_CREAT) == 0 || (oflag & O_EXCL) == 0)
281 fd = __libc_open (finalname,
282 (oflag & ~(O_CREAT|O_ACCMODE)) | O_NOFOLLOW | O_RDWR);
286 /* If we are supposed to create the file try this next. */
287 if ((oflag & O_CREAT) != 0 && errno == ENOENT)
290 /* Return. errno is already set. */
293 /* Check whether we already have this semaphore mapped and
294 create one if necessary. */
295 result = check_add_mapping (name, namelen, fd, SEM_FAILED);
299 /* We have to open a temporary file first since it must have the
300 correct form before we can start using it. */
307 va_start (ap, oflag);
309 mode = va_arg (ap, mode_t);
310 value = va_arg (ap, unsigned int);
314 if (value > SEM_VALUE_MAX)
316 __set_errno (EINVAL);
320 /* Create the initial file content. */
324 struct new_sem newsem;
327 sem.newsem.value = value;
328 sem.newsem.private = 0;
329 sem.newsem.nwaiters = 0;
331 /* Initialize the remaining bytes as well. */
332 memset ((char *) &sem.initsem + sizeof (struct new_sem), '\0',
333 sizeof (sem_t) - sizeof (struct new_sem));
335 tmpfname = (char *) alloca (mountpoint.dirlen + 6 + 1);
336 mempcpy (mempcpy (tmpfname, mountpoint.dir, mountpoint.dirlen),
339 fd = __gen_tempname (tmpfname, __GT_FILE, mode);
343 if (TEMP_FAILURE_RETRY (__libc_write (fd, &sem.initsem, sizeof (sem_t)))
345 /* Map the sem_t structure from the file. */
346 && (result = (sem_t *) mmap (NULL, sizeof (sem_t),
347 PROT_READ | PROT_WRITE, MAP_SHARED,
348 fd, 0)) != MAP_FAILED)
350 /* Create the file. Don't overwrite an existing file. */
351 if (link (tmpfname, finalname) != 0)
353 /* Undo the mapping. */
354 (void) munmap (result, sizeof (sem_t));
356 /* Reinitialize 'result'. */
359 /* This failed. If O_EXCL is not set and the problem was
360 that the file exists, try again. */
361 if ((oflag & O_EXCL) == 0 && errno == EEXIST)
363 /* Remove the file. */
364 (void) unlink (tmpfname);
366 /* Close the file. */
367 (void) __libc_close (fd);
373 /* Insert the mapping into the search tree. This also
374 determines whether another thread sneaked by and already
375 added such a mapping despite the fact that we created it. */
376 result = check_add_mapping (name, namelen, fd, result);
379 /* Now remove the temporary name. This should never fail. If
380 it fails we leak a file name. Better fix the kernel. */
381 (void) unlink (tmpfname);
384 /* Map the mmap error to the error we need. */
385 if (MAP_FAILED != (void *) SEM_FAILED && result == MAP_FAILED)
388 /* We don't need the file descriptor anymore. */
391 /* Do not disturb errno. */
392 INTERNAL_SYSCALL_DECL (err);
393 INTERNAL_SYSCALL (close, err, 1, fd);