Fix Wundef warning for HAVE_STRFTIME
[glibc.git] / nptl / sem_open.c
blobcf91859dabd6da1e776a9599b31bb3c3e3164709
1 /* Copyright (C) 2002-2014 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"
39 /* Information about the mount point. */
40 struct mountpoint_info mountpoint attribute_hidden;
42 /* This is the default mount point. */
43 static const char defaultmount[] = "/dev/shm";
44 /* This is the default directory. */
45 static const char defaultdir[] = "/dev/shm/sem.";
47 /* Protect the `mountpoint' variable above. */
48 pthread_once_t __namedsem_once attribute_hidden = PTHREAD_ONCE_INIT;
51 /* Determine where the shmfs is mounted (if at all). */
52 void
53 attribute_hidden
54 __where_is_shmfs (void)
56 char buf[512];
57 struct statfs f;
58 struct mntent resmem;
59 struct mntent *mp;
60 FILE *fp;
62 /* The canonical place is /dev/shm. This is at least what the
63 documentation tells everybody to do. */
64 if (__statfs (defaultmount, &f) == 0 && (f.f_type == SHMFS_SUPER_MAGIC
65 || f.f_type == RAMFS_MAGIC))
67 /* It is in the normal place. */
68 mountpoint.dir = (char *) defaultdir;
69 mountpoint.dirlen = sizeof (defaultdir) - 1;
71 return;
74 /* OK, do it the hard way. Look through the /proc/mounts file and if
75 this does not exist through /etc/fstab to find the mount point. */
76 fp = __setmntent ("/proc/mounts", "r");
77 if (__glibc_unlikely (fp == NULL))
79 fp = __setmntent (_PATH_MNTTAB, "r");
80 if (__glibc_unlikely (fp == NULL))
81 /* There is nothing we can do. Blind guesses are not helpful. */
82 return;
85 /* Now read the entries. */
86 while ((mp = __getmntent_r (fp, &resmem, buf, sizeof buf)) != NULL)
87 /* The original name is "shm" but this got changed in early Linux
88 2.4.x to "tmpfs". */
89 if (strcmp (mp->mnt_type, "tmpfs") == 0
90 || strcmp (mp->mnt_type, "shm") == 0)
92 /* Found it. There might be more than one place where the
93 filesystem is mounted but one is enough for us. */
94 size_t namelen;
96 /* First make sure this really is the correct entry. At least
97 some versions of the kernel give wrong information because
98 of the implicit mount of the shmfs for SysV IPC. */
99 if (__statfs (mp->mnt_dir, &f) != 0 || (f.f_type != SHMFS_SUPER_MAGIC
100 && f.f_type != RAMFS_MAGIC))
101 continue;
103 namelen = strlen (mp->mnt_dir);
105 if (namelen == 0)
106 /* Hum, maybe some crippled entry. Keep on searching. */
107 continue;
109 mountpoint.dir = (char *) malloc (namelen + 4 + 2);
110 if (mountpoint.dir != NULL)
112 char *cp = __mempcpy (mountpoint.dir, mp->mnt_dir, namelen);
113 if (cp[-1] != '/')
114 *cp++ = '/';
115 cp = stpcpy (cp, "sem.");
116 mountpoint.dirlen = cp - mountpoint.dir;
119 break;
122 /* Close the stream. */
123 __endmntent (fp);
127 /* Comparison function for search of existing mapping. */
129 attribute_hidden
130 __sem_search (const void *a, const void *b)
132 const struct inuse_sem *as = (const struct inuse_sem *) a;
133 const struct inuse_sem *bs = (const struct inuse_sem *) b;
135 if (as->ino != bs->ino)
136 /* Cannot return the difference the type is larger than int. */
137 return as->ino < bs->ino ? -1 : (as->ino == bs->ino ? 0 : 1);
139 if (as->dev != bs->dev)
140 /* Cannot return the difference the type is larger than int. */
141 return as->dev < bs->dev ? -1 : (as->dev == bs->dev ? 0 : 1);
143 return strcmp (as->name, bs->name);
147 /* The search tree for existing mappings. */
148 void *__sem_mappings attribute_hidden;
150 /* Lock to protect the search tree. */
151 int __sem_mappings_lock attribute_hidden = LLL_LOCK_INITIALIZER;
154 /* Search for existing mapping and if possible add the one provided. */
155 static sem_t *
156 check_add_mapping (const char *name, size_t namelen, int fd, sem_t *existing)
158 sem_t *result = SEM_FAILED;
160 /* Get the information about the file. */
161 struct stat64 st;
162 if (__fxstat64 (_STAT_VER, fd, &st) == 0)
164 /* Get the lock. */
165 lll_lock (__sem_mappings_lock, LLL_PRIVATE);
167 /* Search for an existing mapping given the information we have. */
168 struct inuse_sem *fake;
169 fake = (struct inuse_sem *) alloca (sizeof (*fake) + namelen);
170 memcpy (fake->name, name, namelen);
171 fake->dev = st.st_dev;
172 fake->ino = st.st_ino;
174 struct inuse_sem **foundp = tfind (fake, &__sem_mappings, __sem_search);
175 if (foundp != NULL)
177 /* There is already a mapping. Use it. */
178 result = (*foundp)->sem;
179 ++(*foundp)->refcnt;
181 else
183 /* We haven't found a mapping. Install ione. */
184 struct inuse_sem *newp;
186 newp = (struct inuse_sem *) malloc (sizeof (*newp) + namelen);
187 if (newp != NULL)
189 /* If the caller hasn't provided any map it now. */
190 if (existing == SEM_FAILED)
191 existing = (sem_t *) mmap (NULL, sizeof (sem_t),
192 PROT_READ | PROT_WRITE, MAP_SHARED,
193 fd, 0);
195 newp->dev = st.st_dev;
196 newp->ino = st.st_ino;
197 newp->refcnt = 1;
198 newp->sem = existing;
199 memcpy (newp->name, name, namelen);
201 /* Insert the new value. */
202 if (existing != MAP_FAILED
203 && tsearch (newp, &__sem_mappings, __sem_search) != NULL)
204 /* Successful. */
205 result = existing;
206 else
207 /* Something went wrong while inserting the new
208 value. We fail completely. */
209 free (newp);
213 /* Release the lock. */
214 lll_unlock (__sem_mappings_lock, LLL_PRIVATE);
217 if (result != existing && existing != SEM_FAILED && existing != MAP_FAILED)
219 /* Do not disturb errno. */
220 INTERNAL_SYSCALL_DECL (err);
221 INTERNAL_SYSCALL (munmap, err, 2, existing, sizeof (sem_t));
224 return result;
228 sem_t *
229 sem_open (const char *name, int oflag, ...)
231 char *finalname;
232 sem_t *result = SEM_FAILED;
233 int fd;
235 /* Determine where the shmfs is mounted. */
236 __pthread_once (&__namedsem_once, __where_is_shmfs);
238 /* If we don't know the mount points there is nothing we can do. Ever. */
239 if (mountpoint.dir == NULL)
241 __set_errno (ENOSYS);
242 return SEM_FAILED;
245 /* Construct the filename. */
246 while (name[0] == '/')
247 ++name;
249 if (name[0] == '\0')
251 /* The name "/" is not supported. */
252 __set_errno (EINVAL);
253 return SEM_FAILED;
255 size_t namelen = strlen (name) + 1;
257 /* Create the name of the final file. */
258 finalname = (char *) alloca (mountpoint.dirlen + namelen);
259 __mempcpy (__mempcpy (finalname, mountpoint.dir, mountpoint.dirlen),
260 name, namelen);
262 /* If the semaphore object has to exist simply open it. */
263 if ((oflag & O_CREAT) == 0 || (oflag & O_EXCL) == 0)
265 try_again:
266 fd = __libc_open (finalname,
267 (oflag & ~(O_CREAT|O_ACCMODE)) | O_NOFOLLOW | O_RDWR);
269 if (fd == -1)
271 /* If we are supposed to create the file try this next. */
272 if ((oflag & O_CREAT) != 0 && errno == ENOENT)
273 goto try_create;
275 /* Return. errno is already set. */
277 else
278 /* Check whether we already have this semaphore mapped and
279 create one if necessary. */
280 result = check_add_mapping (name, namelen, fd, SEM_FAILED);
282 else
284 /* We have to open a temporary file first since it must have the
285 correct form before we can start using it. */
286 char *tmpfname;
287 mode_t mode;
288 unsigned int value;
289 va_list ap;
291 try_create:
292 va_start (ap, oflag);
294 mode = va_arg (ap, mode_t);
295 value = va_arg (ap, unsigned int);
297 va_end (ap);
299 if (value > SEM_VALUE_MAX)
301 __set_errno (EINVAL);
302 return SEM_FAILED;
305 /* Create the initial file content. */
306 union
308 sem_t initsem;
309 struct new_sem newsem;
310 } sem;
312 sem.newsem.value = value;
313 sem.newsem.private = 0;
314 sem.newsem.nwaiters = 0;
316 /* Initialize the remaining bytes as well. */
317 memset ((char *) &sem.initsem + sizeof (struct new_sem), '\0',
318 sizeof (sem_t) - sizeof (struct new_sem));
320 tmpfname = (char *) alloca (mountpoint.dirlen + 6 + 1);
321 char *xxxxxx = __mempcpy (tmpfname, mountpoint.dir, mountpoint.dirlen);
323 int retries = 0;
324 #define NRETRIES 50
325 while (1)
327 /* Add the suffix for mktemp. */
328 strcpy (xxxxxx, "XXXXXX");
330 /* We really want to use mktemp here. We cannot use mkstemp
331 since the file must be opened with a specific mode. The
332 mode cannot later be set since then we cannot apply the
333 file create mask. */
334 if (__mktemp (tmpfname) == NULL)
335 return SEM_FAILED;
337 /* Open the file. Make sure we do not overwrite anything. */
338 fd = __libc_open (tmpfname, O_RDWR | O_CREAT | O_EXCL, mode);
339 if (fd == -1)
341 if (errno == EEXIST)
343 if (++retries < NRETRIES)
344 continue;
346 __set_errno (EAGAIN);
349 return SEM_FAILED;
352 /* We got a file. */
353 break;
356 if (TEMP_FAILURE_RETRY (__libc_write (fd, &sem.initsem, sizeof (sem_t)))
357 == sizeof (sem_t)
358 /* Map the sem_t structure from the file. */
359 && (result = (sem_t *) mmap (NULL, sizeof (sem_t),
360 PROT_READ | PROT_WRITE, MAP_SHARED,
361 fd, 0)) != MAP_FAILED)
363 /* Create the file. Don't overwrite an existing file. */
364 if (link (tmpfname, finalname) != 0)
366 /* Undo the mapping. */
367 (void) munmap (result, sizeof (sem_t));
369 /* Reinitialize 'result'. */
370 result = SEM_FAILED;
372 /* This failed. If O_EXCL is not set and the problem was
373 that the file exists, try again. */
374 if ((oflag & O_EXCL) == 0 && errno == EEXIST)
376 /* Remove the file. */
377 (void) unlink (tmpfname);
379 /* Close the file. */
380 (void) __libc_close (fd);
382 goto try_again;
385 else
386 /* Insert the mapping into the search tree. This also
387 determines whether another thread sneaked by and already
388 added such a mapping despite the fact that we created it. */
389 result = check_add_mapping (name, namelen, fd, result);
392 /* Now remove the temporary name. This should never fail. If
393 it fails we leak a file name. Better fix the kernel. */
394 (void) unlink (tmpfname);
397 /* Map the mmap error to the error we need. */
398 if (MAP_FAILED != (void *) SEM_FAILED && result == MAP_FAILED)
399 result = SEM_FAILED;
401 /* We don't need the file descriptor anymore. */
402 if (fd != -1)
404 /* Do not disturb errno. */
405 INTERNAL_SYSCALL_DECL (err);
406 INTERNAL_SYSCALL (close, err, 1, fd);
409 return result;