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"
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
50 /* Information about the mount point. */
51 struct mountpoint_info mountpoint attribute_hidden;
53 /* This is the default mount point. */
54 static const char defaultmount[] = "/dev/shm";
55 /* This is the default directory. */
56 static const char defaultdir[] = "/dev/shm/sem.";
58 /* Protect the `mountpoint' variable above. */
59 pthread_once_t __namedsem_once attribute_hidden = PTHREAD_ONCE_INIT;
62 /* Determine where the shmfs is mounted (if at all). */
65 __where_is_shmfs (void)
73 /* The canonical place is /dev/shm. This is at least what the
74 documentation tells everybody to do. */
75 if (__statfs (defaultmount, &f) == 0 && f.f_type == SHMFS_SUPER_MAGIC)
77 /* It is in the normal place. */
78 mountpoint.dir = (char *) defaultdir;
79 mountpoint.dirlen = sizeof (defaultdir) - 1;
84 /* OK, do it the hard way. Look through the /proc/mounts file and if
85 this does not exist through /etc/fstab to find the mount point. */
86 fp = __setmntent ("/proc/mounts", "r");
87 if (__builtin_expect (fp == NULL, 0))
89 fp = __setmntent (_PATH_MNTTAB, "r");
90 if (__builtin_expect (fp == NULL, 0))
91 /* There is nothing we can do. Blind guesses are not helpful. */
95 /* Now read the entries. */
96 while ((mp = __getmntent_r (fp, &resmem, buf, sizeof buf)) != NULL)
97 /* The original name is "shm" but this got changed in early Linux
99 if (strcmp (mp->mnt_type, "tmpfs") == 0
100 || strcmp (mp->mnt_type, "shm") == 0)
102 /* Found it. There might be more than one place where the
103 filesystem is mounted but one is enough for us. */
106 /* First make sure this really is the correct entry. At least
107 some versions of the kernel give wrong information because
108 of the implicit mount of the shmfs for SysV IPC. */
109 if (__statfs (mp->mnt_dir, &f) != 0 || f.f_type != SHMFS_SUPER_MAGIC)
112 namelen = strlen (mp->mnt_dir);
115 /* Hum, maybe some crippled entry. Keep on searching. */
118 mountpoint.dir = (char *) malloc (namelen + 4 + 2);
119 if (mountpoint.dir != NULL)
121 char *cp = mempcpy (mountpoint.dir, mp->mnt_dir, namelen);
124 cp = stpcpy (cp, "sem.");
125 mountpoint.dirlen = cp - mountpoint.dir;
131 /* Close the stream. */
136 /* Comparison function for search of existing mapping. */
139 __sem_search (const void *a, const void *b)
141 const struct inuse_sem *as = (const struct inuse_sem *) a;
142 const struct inuse_sem *bs = (const struct inuse_sem *) b;
144 if (as->ino != bs->ino)
145 /* Cannot return the difference the type is larger than int. */
146 return as->ino < bs->ino ? -1 : (as->ino == bs->ino ? 0 : 1);
148 if (as->dev != bs->dev)
149 /* Cannot return the difference the type is larger than int. */
150 return as->dev < bs->dev ? -1 : (as->dev == bs->dev ? 0 : 1);
152 return strcmp (as->name, bs->name);
156 /* The search tree for existing mappings. */
157 void *__sem_mappings attribute_hidden;
159 /* Lock to protect the search tree. */
160 lll_lock_t __sem_mappings_lock = LLL_LOCK_INITIALIZER;
163 /* Search for existing mapping and if possible add the one provided. */
165 check_add_mapping (const char *name, size_t namelen, int fd, sem_t *existing)
167 sem_t *result = SEM_FAILED;
169 /* Get the information about the file. */
170 #ifdef __UCLIBC_HAS_LFS__
172 if (__fxstat64 (_STAT_VER, fd, &st) == 0)
175 if (fstat (fd, &st) == 0)
179 lll_lock (__sem_mappings_lock);
181 /* Search for an existing mapping given the information we have. */
182 struct inuse_sem *fake;
183 fake = (struct inuse_sem *) alloca (sizeof (*fake) + namelen);
184 memcpy (fake->name, name, namelen);
185 fake->dev = st.st_dev;
186 fake->ino = st.st_ino;
188 struct inuse_sem **foundp = tfind (fake, &__sem_mappings, __sem_search);
191 /* There is already a mapping. Use it. */
192 result = (*foundp)->sem;
197 /* We haven't found a mapping. Install ione. */
198 struct inuse_sem *newp;
200 newp = (struct inuse_sem *) malloc (sizeof (*newp) + namelen);
203 /* If the caller hasn't provided any map it now. */
204 if (existing == SEM_FAILED)
205 existing = (sem_t *) mmap (NULL, sizeof (sem_t),
206 PROT_READ | PROT_WRITE, MAP_SHARED,
209 newp->dev = st.st_dev;
210 newp->ino = st.st_ino;
212 newp->sem = existing;
213 memcpy (newp->name, name, namelen);
215 /* Insert the new value. */
216 if (existing != MAP_FAILED
217 && tsearch (newp, &__sem_mappings, __sem_search) != NULL)
221 /* Something went wrong while inserting the new
222 value. We fail completely. */
227 /* Release the lock. */
228 lll_unlock (__sem_mappings_lock);
231 if (result != existing && existing != SEM_FAILED && existing != MAP_FAILED)
233 /* Do not disturb errno. */
234 INTERNAL_SYSCALL_DECL (err);
235 INTERNAL_SYSCALL (munmap, err, 2, existing, sizeof (sem_t));
243 sem_open (const char *name, int oflag, ...)
246 sem_t *result = SEM_FAILED;
249 /* Determine where the shmfs is mounted. */
250 __pthread_once (&__namedsem_once, __where_is_shmfs);
252 /* If we don't know the mount points there is nothing we can do. Ever. */
253 if (mountpoint.dir == NULL)
255 __set_errno (ENOSYS);
259 /* Construct the filename. */
260 while (name[0] == '/')
265 /* The name "/" is not supported. */
266 __set_errno (EINVAL);
269 size_t namelen = strlen (name) + 1;
271 /* Create the name of the final file. */
272 finalname = (char *) alloca (mountpoint.dirlen + namelen);
273 mempcpy (mempcpy (finalname, mountpoint.dir, mountpoint.dirlen),
276 /* If the semaphore object has to exist simply open it. */
277 if ((oflag & O_CREAT) == 0 || (oflag & O_EXCL) == 0)
280 fd = __libc_open (finalname,
281 (oflag & ~(O_CREAT|O_ACCMODE)) | O_NOFOLLOW | O_RDWR);
285 /* If we are supposed to create the file try this next. */
286 if ((oflag & O_CREAT) != 0 && errno == ENOENT)
289 /* Return. errno is already set. */
292 /* Check whether we already have this semaphore mapped and
293 create one if necessary. */
294 result = check_add_mapping (name, namelen, fd, SEM_FAILED);
298 /* We have to open a temporary file first since it must have the
299 correct form before we can start using it. */
306 va_start (ap, oflag);
308 mode = va_arg (ap, mode_t);
309 value = va_arg (ap, unsigned int);
313 if (value > SEM_VALUE_MAX)
315 __set_errno (EINVAL);
319 /* Create the initial file content. */
322 struct sem *iinitsem = (struct sem *) &initsem;
323 iinitsem->count = value;
325 /* Initialize the remaining bytes as well. */
326 memset ((char *) &initsem + sizeof (struct sem), '\0',
327 sizeof (sem_t) - sizeof (struct sem));
329 tmpfname = (char *) alloca (mountpoint.dirlen + 6 + 1);
330 char *xxxxxx = mempcpy (tmpfname, mountpoint.dir, mountpoint.dirlen);
331 strcpy (xxxxxx, "XXXXXX");
333 fd = __gen_tempname (tmpfname, __GT_FILE, mode);
337 if (TEMP_FAILURE_RETRY (__libc_write (fd, &initsem, sizeof (sem_t)))
339 /* Map the sem_t structure from the file. */
340 && (result = (sem_t *) mmap (NULL, sizeof (sem_t),
341 PROT_READ | PROT_WRITE, MAP_SHARED,
342 fd, 0)) != MAP_FAILED)
344 /* Create the file. Don't overwrite an existing file. */
345 if (link (tmpfname, finalname) != 0)
347 /* Undo the mapping. */
348 (void) munmap (result, sizeof (sem_t));
350 /* Reinitialize 'result'. */
353 /* This failed. If O_EXCL is not set and the problem was
354 that the file exists, try again. */
355 if ((oflag & O_EXCL) == 0 && errno == EEXIST)
357 /* Remove the file. */
358 (void) unlink (tmpfname);
360 /* Close the file. */
361 (void) __libc_close (fd);
367 /* Insert the mapping into the search tree. This also
368 determines whether another thread sneaked by and already
369 added such a mapping despite the fact that we created it. */
370 result = check_add_mapping (name, namelen, fd, result);
373 /* Now remove the temporary name. This should never fail. If
374 it fails we leak a file name. Better fix the kernel. */
375 (void) unlink (tmpfname);
378 /* Map the mmap error to the error we need. */
379 if (MAP_FAILED != (void *) SEM_FAILED && result == MAP_FAILED)
382 /* We don't need the file descriptor anymore. */
385 /* Do not disturb errno. */
386 INTERNAL_SYSCALL_DECL (err);
387 INTERNAL_SYSCALL (close, err, 1, fd);