Initial revision
[kopensolaris-gnu/glibc.git] / nptl / sem_open.c
1 /* Copyright (C) 2002 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
4
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.
9
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.
14
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.  */
19
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"
36
37
38
39 /* Information about the mount point.  */
40 struct mountpoint_info mountpoint attribute_hidden;
41
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.";
46
47 /* Protect the `mountpoint' variable above.  */
48 pthread_once_t __namedsem_once attribute_hidden = PTHREAD_ONCE_INIT;
49
50
51 /* Determine where the shmfs is mounted (if at all).  */
52 void
53 attribute_hidden
54 __where_is_shmfs (void)
55 {
56   char buf[512];
57   struct statfs f;
58   struct mntent resmem;
59   struct mntent *mp;
60   FILE *fp;
61
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     {
66       /* It is in the normal place.  */
67       mountpoint.dir = (char *) defaultdir;
68       mountpoint.dirlen = sizeof (defaultdir) - 1;
69
70       return;
71     }
72
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))
77     {
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;
82     }
83
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)
90       {
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;
94
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;
100
101         namelen = strlen (mp->mnt_dir);
102
103         if (namelen == 0)
104           /* Hum, maybe some crippled entry.  Keep on searching.  */
105           continue;
106
107         mountpoint.dir = (char *) malloc (namelen + 4 + 2);
108         if (mountpoint.dir != NULL)
109           {
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;
115           }
116
117         break;
118       }
119
120   /* Close the stream.  */
121   __endmntent (fp);
122 }
123
124
125 sem_t *
126 sem_open (const char *name, int oflag, ...)
127 {
128   char *finalname;
129   size_t namelen;
130   sem_t *result;
131   int fd;
132
133   /* Determine where the shmfs is mounted.  */
134   pthread_once (&__namedsem_once, __where_is_shmfs);
135
136   /* If we don't know the mount points there is nothing we can do.  Ever.  */
137   if (mountpoint.dir == NULL)
138     {
139       __set_errno (ENOSYS);
140       return SEM_FAILED;
141     }
142
143   /* Construct the filename.  */
144   while (name[0] == '/')
145     ++name;
146
147   if (name[0] == '\0')
148     {
149       /* The name "/" is not supported.  */
150       __set_errno (EINVAL);
151       return SEM_FAILED;
152     }
153   namelen = strlen (name);
154
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);
159
160   /* If the semaphore object has to exist simply open it.  */
161   if ((oflag & O_CREAT) == 0)
162     {
163       fd = open (finalname, oflag | O_NOFOLLOW);
164
165       if (fd == -1)
166         /* Return.  errno is already set.  */
167         return SEM_FAILED;
168
169       /* Map the sem_t structure from the file.  */
170       result = (sem_t *) mmap (NULL, sizeof (sem_t), PROT_READ | PROT_WRITE,
171                                MAP_SHARED, fd, 0);
172     }
173   else
174     {
175       /* We have to open a temporary file first since it must have the
176          correct form before we can start using it.  */
177       char *tmpfname;
178       mode_t mode;
179       unsigned int value;
180       va_list ap;
181
182       va_start (ap, oflag);
183
184       mode = va_arg (ap, mode_t);
185       value = va_arg (ap, unsigned int);
186
187       va_end (ap);
188
189       if (value > SEM_VALUE_MAX)
190         {
191           __set_errno (EINVAL);
192           return SEM_FAILED;
193         }
194
195       tmpfname = (char *) alloca (mountpoint.dirlen + 6 + 1);
196       strcpy (__mempcpy (tmpfname, mountpoint.dir, mountpoint.dirlen),
197               "XXXXXX");
198
199       fd = mkstemp (tmpfname);
200       if (fd == -1)
201         return SEM_FAILED;
202
203       /* Create the initial file content.  */
204       sem_t initsem;
205
206       struct sem *iinitsem = (struct sem *) &initsem;
207       iinitsem->count = value;
208
209       /* Initialize the remaining bytes as well.  */
210       memset ((char *) &initsem + sizeof (struct sem), '\0',
211               sizeof (sem_t) - sizeof (struct sem));
212
213       if (TEMP_FAILURE_RETRY (write (fd, &initsem, sizeof (sem_t)))
214           != sizeof (sem_t)
215           /* Adjust the permission.  */
216           || fchmod (fd, mode) != 0)
217         {
218         unlink_return:
219           unlink (tmpfname);
220           return SEM_FAILED;
221         }
222
223       /* Map the sem_t structure from the file.  */
224       result = (sem_t *) mmap (NULL, sizeof (sem_t), PROT_READ | PROT_WRITE,
225                                MAP_SHARED, fd, 0);
226       if (result == MAP_FAILED)
227         goto unlink_return;
228
229       /* Create or overwrite the file.  Depending on what is wanted we
230          use rename or link.  */
231       if ((oflag & O_EXCL) == 0)
232         {
233           /* An existing file gets overwritten.  */
234           if (rename (tmpfname, finalname) != 0)
235             {
236             unmap_unlink_return:
237               munmap (result, sizeof (sem_t));
238               goto unlink_return;
239             }
240         }
241       else
242         {
243           /* Don't overwrite an existing file.  */
244           if (link (tmpfname, finalname) != 0)
245             goto unmap_unlink_return;
246
247           /* This went well.  Now remove the temporary name.  This
248              should never fail.  If it fails we leak a file name.
249              Better fix the kernel.  */
250           (void) unlink (tmpfname);
251         }
252     }
253
254   /* We don't need the file descriptor anymore.  */
255   close (fd);
256
257   return result;
258 }