NPTL/arc: notify kernel of the TP value
[uClibc.git] / libpthread / nptl / sem_open.c
blob3a72079081438d838416f107ff1819b3a400a036
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, see
17 <http://www.gnu.org/licenses/>. */
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <mntent.h>
22 #include <paths.h>
23 #include <pthread.h>
24 #include <search.h>
25 #include <semaphore.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/mman.h>
32 #include <sys/stat.h>
33 #include <sys/statfs.h>
34 #include <linux_fsinfo.h>
35 #include "semaphoreP.h"
36 #include "../../libc/misc/internals/tempname.h"
39 /* Compatibility defines. */
40 #define __endmntent endmntent
41 #define __getmntent_r getmntent_r
42 #define __setmntent setmntent
43 #define __statfs statfs
44 #define __libc_close close
45 #ifdef __UCLIBC_HAS_LFS__
46 # define __libc_open open64
47 # define __fxstat64(vers, fd, buf) fstat64(fd, buf)
48 #else
49 # define __libc_open open
50 # define __fxstat64(vers, fd, buf) fstat(fd, buf)
51 # define stat64 stat
52 #endif
53 #define __libc_write write
56 /* Information about the mount point. */
57 struct mountpoint_info mountpoint attribute_hidden;
59 /* This is the default mount point. */
60 static const char defaultmount[] = "/dev/shm";
61 /* This is the default directory. */
62 static const char defaultdir[] = "/dev/shm/sem.";
64 /* Protect the `mountpoint' variable above. */
65 pthread_once_t __namedsem_once attribute_hidden = PTHREAD_ONCE_INIT;
68 /* Determine where the shmfs is mounted (if at all). */
69 void
70 attribute_hidden
71 __where_is_shmfs (void)
73 char buf[512];
74 struct statfs f;
75 struct mntent resmem;
76 struct mntent *mp;
77 FILE *fp;
79 /* The canonical place is /dev/shm. This is at least what the
80 documentation tells everybody to do. */
81 if (__statfs (defaultmount, &f) == 0 && f.f_type == SHMFS_SUPER_MAGIC)
83 /* It is in the normal place. */
84 mountpoint.dir = (char *) defaultdir;
85 mountpoint.dirlen = sizeof (defaultdir) - 1;
87 return;
90 /* OK, do it the hard way. Look through the /proc/mounts file and if
91 this does not exist through /etc/fstab to find the mount point. */
92 fp = __setmntent ("/proc/mounts", "r");
93 if (unlikely (fp == NULL))
95 fp = __setmntent (_PATH_MNTTAB, "r");
96 if (unlikely (fp == NULL))
97 /* There is nothing we can do. Blind guesses are not helpful. */
98 return;
101 /* Now read the entries. */
102 while ((mp = __getmntent_r (fp, &resmem, buf, sizeof buf)) != NULL)
103 /* The original name is "shm" but this got changed in early Linux
104 2.4.x to "tmpfs". */
105 if (strcmp (mp->mnt_type, "tmpfs") == 0
106 || strcmp (mp->mnt_type, "shm") == 0)
108 /* Found it. There might be more than one place where the
109 filesystem is mounted but one is enough for us. */
110 size_t namelen;
112 /* First make sure this really is the correct entry. At least
113 some versions of the kernel give wrong information because
114 of the implicit mount of the shmfs for SysV IPC. */
115 if (__statfs (mp->mnt_dir, &f) != 0 || f.f_type != SHMFS_SUPER_MAGIC)
116 continue;
118 namelen = strlen (mp->mnt_dir);
120 if (namelen == 0)
121 /* Hum, maybe some crippled entry. Keep on searching. */
122 continue;
124 mountpoint.dir = (char *) malloc (namelen + 4 + 2);
125 if (mountpoint.dir != NULL)
127 char *cp = mempcpy (mountpoint.dir, mp->mnt_dir, namelen);
128 if (cp[-1] != '/')
129 *cp++ = '/';
130 cp = stpcpy (cp, "sem.");
131 mountpoint.dirlen = cp - mountpoint.dir;
134 break;
137 /* Close the stream. */
138 __endmntent (fp);
142 /* Comparison function for search of existing mapping. */
144 attribute_hidden
145 __sem_search (const void *a, const void *b)
147 const struct inuse_sem *as = (const struct inuse_sem *) a;
148 const struct inuse_sem *bs = (const struct inuse_sem *) b;
150 if (as->ino != bs->ino)
151 /* Cannot return the difference the type is larger than int. */
152 return as->ino < bs->ino ? -1 : (as->ino == bs->ino ? 0 : 1);
154 if (as->dev != bs->dev)
155 /* Cannot return the difference the type is larger than int. */
156 return as->dev < bs->dev ? -1 : (as->dev == bs->dev ? 0 : 1);
158 return strcmp (as->name, bs->name);
162 /* The search tree for existing mappings. */
163 void *__sem_mappings attribute_hidden;
165 /* Lock to protect the search tree. */
166 int __sem_mappings_lock attribute_hidden = LLL_LOCK_INITIALIZER;
169 /* Search for existing mapping and if possible add the one provided. */
170 static sem_t *
171 check_add_mapping (const char *name, size_t namelen, int fd, sem_t *existing)
173 sem_t *result = SEM_FAILED;
175 /* Get the information about the file. */
176 struct stat64 st;
177 if (__fxstat64 (_STAT_VER, fd, &st) == 0)
179 /* Get the lock. */
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);
190 if (foundp != NULL)
192 /* There is already a mapping. Use it. */
193 result = (*foundp)->sem;
194 ++(*foundp)->refcnt;
196 else
198 /* We haven't found a mapping. Install ione. */
199 struct inuse_sem *newp;
201 newp = (struct inuse_sem *) malloc (sizeof (*newp) + namelen);
202 if (newp != NULL)
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,
208 fd, 0);
210 newp->dev = st.st_dev;
211 newp->ino = st.st_ino;
212 newp->refcnt = 1;
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)
219 /* Successful. */
220 result = existing;
221 else
222 /* Something went wrong while inserting the new
223 value. We fail completely. */
224 free (newp);
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));
239 return result;
243 sem_t *
244 sem_open (const char *name, int oflag, ...)
246 char *finalname;
247 sem_t *result = SEM_FAILED;
248 int fd;
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);
257 return SEM_FAILED;
260 /* Construct the filename. */
261 while (name[0] == '/')
262 ++name;
264 if (name[0] == '\0')
266 /* The name "/" is not supported. */
267 __set_errno (EINVAL);
268 return SEM_FAILED;
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),
275 name, namelen);
277 /* If the semaphore object has to exist simply open it. */
278 if ((oflag & O_CREAT) == 0 || (oflag & O_EXCL) == 0)
280 try_again:
281 fd = __libc_open (finalname,
282 (oflag & ~(O_CREAT|O_ACCMODE)) | O_NOFOLLOW | O_RDWR);
284 if (fd == -1)
286 /* If we are supposed to create the file try this next. */
287 if ((oflag & O_CREAT) != 0 && errno == ENOENT)
288 goto try_create;
290 /* Return. errno is already set. */
292 else
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);
297 else
299 /* We have to open a temporary file first since it must have the
300 correct form before we can start using it. */
301 char *tmpfname;
302 mode_t mode;
303 unsigned int value;
304 va_list ap;
306 try_create:
307 va_start (ap, oflag);
309 mode = va_arg (ap, mode_t);
310 value = va_arg (ap, unsigned int);
312 va_end (ap);
314 if (value > SEM_VALUE_MAX)
316 __set_errno (EINVAL);
317 return SEM_FAILED;
320 /* Create the initial file content. */
321 union
323 sem_t initsem;
324 struct new_sem newsem;
325 } sem;
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),
337 "XXXXXX", 7);
339 fd = __gen_tempname (tmpfname, __GT_FILE, 0, mode);
340 if (fd == -1)
341 return SEM_FAILED;
343 if (TEMP_FAILURE_RETRY (__libc_write (fd, &sem.initsem, sizeof (sem_t)))
344 == 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'. */
357 result = SEM_FAILED;
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);
369 goto try_again;
372 else
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)
386 result = SEM_FAILED;
388 /* We don't need the file descriptor anymore. */
389 if (fd != -1)
391 /* Do not disturb errno. */
392 INTERNAL_SYSCALL_DECL (err);
393 INTERNAL_SYSCALL (close, err, 1, fd);
396 return result;