2003-05-16 Roland McGrath <roland@redhat.com>
[glibc.git] / nptl / sem_open.c
blob2bfea63a807f64419dc8e83852032d74d2535320
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
18 02111-1307 USA. */
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <mntent.h>
23 #include <paths.h>
24 #include <pthread.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)
66 /* It is in the normal place. */
67 mountpoint.dir = (char *) defaultdir;
68 mountpoint.dirlen = sizeof (defaultdir) - 1;
70 return;
73 /* OK, do it the hard way. Look through the /proc/mounts file and if
74 this does not exist through /etc/fstab to find the mount point. */
75 fp = __setmntent ("/proc/mounts", "r");
76 if (__builtin_expect (fp == NULL, 0))
78 fp = __setmntent (_PATH_MNTTAB, "r");
79 if (__builtin_expect (fp == NULL, 0))
80 /* There is nothing we can do. Blind guesses are not helpful. */
81 return;
84 /* Now read the entries. */
85 while ((mp = __getmntent_r (fp, &resmem, buf, sizeof buf)) != NULL)
86 /* The original name is "shm" but this got changed in early Linux
87 2.4.x to "tmpfs". */
88 if (strcmp (mp->mnt_type, "tmpfs") == 0
89 || strcmp (mp->mnt_type, "shm") == 0)
91 /* Found it. There might be more than one place where the
92 filesystem is mounted but one is enough for us. */
93 size_t namelen;
95 /* First make sure this really is the correct entry. At least
96 some versions of the kernel give wrong information because
97 of the implicit mount of the shmfs for SysV IPC. */
98 if (__statfs (mp->mnt_dir, &f) != 0 || f.f_type != SHMFS_SUPER_MAGIC)
99 continue;
101 namelen = strlen (mp->mnt_dir);
103 if (namelen == 0)
104 /* Hum, maybe some crippled entry. Keep on searching. */
105 continue;
107 mountpoint.dir = (char *) malloc (namelen + 4 + 2);
108 if (mountpoint.dir != NULL)
110 char *cp = __mempcpy (mountpoint.dir, mp->mnt_dir, namelen);
111 if (cp[-1] != '/')
112 *cp++ = '/';
113 cp = stpcpy (cp, "sem.");
114 mountpoint.dirlen = cp - mountpoint.dir;
117 break;
120 /* Close the stream. */
121 __endmntent (fp);
125 sem_t *
126 sem_open (const char *name, int oflag, ...)
128 char *finalname;
129 size_t namelen;
130 sem_t *result = SEM_FAILED;
131 int fd;
133 /* Determine where the shmfs is mounted. */
134 INTUSE(__pthread_once) (&__namedsem_once, __where_is_shmfs);
136 /* If we don't know the mount points there is nothing we can do. Ever. */
137 if (mountpoint.dir == NULL)
139 __set_errno (ENOSYS);
140 return SEM_FAILED;
143 /* Construct the filename. */
144 while (name[0] == '/')
145 ++name;
147 if (name[0] == '\0')
149 /* The name "/" is not supported. */
150 __set_errno (EINVAL);
151 return SEM_FAILED;
153 namelen = strlen (name);
155 /* Create the name of the final file. */
156 finalname = (char *) alloca (mountpoint.dirlen + namelen + 1);
157 __mempcpy (__mempcpy (finalname, mountpoint.dir, mountpoint.dirlen),
158 name, namelen + 1);
160 /* If the semaphore object has to exist simply open it. */
161 if ((oflag & O_CREAT) == 0 || (oflag & O_EXCL) == 0)
163 try_again:
164 fd = __libc_open (finalname,
165 (oflag & ~(O_CREAT|O_ACCMODE)) | O_NOFOLLOW | O_RDWR);
167 if (fd == -1)
169 /* If we are supposed to create the file try this next. */
170 if ((oflag & O_CREAT) != 0)
171 goto try_create;
173 /* Return. errno is already set. */
175 else
176 /* Map the sem_t structure from the file. */
177 result = (sem_t *) mmap (NULL, sizeof (sem_t), PROT_READ | PROT_WRITE,
178 MAP_SHARED, fd, 0);
180 else
182 /* We have to open a temporary file first since it must have the
183 correct form before we can start using it. */
184 char *tmpfname;
185 mode_t mode;
186 unsigned int value;
187 va_list ap;
189 try_create:
190 va_start (ap, oflag);
192 mode = va_arg (ap, mode_t);
193 value = va_arg (ap, unsigned int);
195 va_end (ap);
197 if (value > SEM_VALUE_MAX)
199 __set_errno (EINVAL);
200 return SEM_FAILED;
203 tmpfname = (char *) alloca (mountpoint.dirlen + 6 + 1);
204 strcpy (__mempcpy (tmpfname, mountpoint.dir, mountpoint.dirlen),
205 "XXXXXX");
207 fd = mkstemp (tmpfname);
208 if (fd == -1)
209 return SEM_FAILED;
211 /* Create the initial file content. */
212 sem_t initsem;
214 struct sem *iinitsem = (struct sem *) &initsem;
215 iinitsem->count = value;
217 /* Initialize the remaining bytes as well. */
218 memset ((char *) &initsem + sizeof (struct sem), '\0',
219 sizeof (sem_t) - sizeof (struct sem));
221 if (TEMP_FAILURE_RETRY (__libc_write (fd, &initsem, sizeof (sem_t)))
222 == sizeof (sem_t)
223 /* Adjust the permission. */
224 && fchmod (fd, mode) == 0
225 /* Map the sem_t structure from the file. */
226 && (result = (sem_t *) mmap (NULL, sizeof (sem_t),
227 PROT_READ | PROT_WRITE, MAP_SHARED,
228 fd, 0)) != MAP_FAILED)
230 /* Create the file. Don't overwrite an existing file. */
231 if (link (tmpfname, finalname) != 0)
233 /* Undo the mapping. */
234 (void) munmap (result, sizeof (sem_t));
236 /* Reinitialize 'result'. */
237 result = SEM_FAILED;
239 /* This failed. If O_EXCL is not set and the problem was
240 that the file exists, try again. */
241 if ((oflag & O_EXCL) == 0 && errno == EEXIST)
243 /* Remove the file. */
244 (void) unlink (tmpfname);
246 /* Close the file. */
247 (void) __libc_close (fd);
249 goto try_again;
254 /* Now remove the temporary name. This should never fail. If
255 it fails we leak a file name. Better fix the kernel. */
256 (void) unlink (tmpfname);
259 /* Map the mmap error to the error we need. */
260 if (MAP_FAILED != (void *) SEM_FAILED && result == MAP_FAILED)
261 result = SEM_FAILED;
263 /* We don't need the file descriptor anymore. */
264 if (fd != -1)
265 (void) __libc_close (fd);
267 return result;