source: trunk/libs/newlib/src/newlib/libc/sys/linux/mq_open.c @ 444

Last change on this file since 444 was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

File size: 8.7 KB
Line 
1/* Copyright 2002, Red Hat Inc. */
2
3#include <mqueue.h>
4#include <fcntl.h>
5#include <unistd.h>
6#include <errno.h>
7#include <sys/stat.h>
8#include <sys/mman.h>
9#include <sys/ipc.h>
10#include <sys/sem.h>
11#include <string.h>
12#include <stdlib.h>
13#include <time.h>
14#include <stdarg.h>
15#include <machine/weakalias.h>
16#define _LIBC 1
17#include <sys/lock.h>
18#undef _LIBC
19
20#include "mqlocal.h"
21
22#define NHASH   32                /* Num of hash lists, must be a power of 2 */
23#define LOCHASH(i)      ((i)&(NHASH-1))
24
25static long     mq_index;       /* Index of next entry */
26static struct   libc_mq *mq_hash[NHASH];   /* Hash list heads for mqopen_infos */
27
28__LOCK_INIT(static, mq_hash_lock);
29
30mqd_t
31mq_open (const char *name, int oflag, ...)
32{
33  MSG *wrbuf = NULL;
34  MSG *rdbuf = NULL;
35  int msgqid = -1;
36  int rc = -1;
37  int fd = -1;
38  int semid = -1;
39  int created = 0;
40  key_t key = (key_t)-1;
41  struct mq_attr *attr = (struct mq_attr *)MAP_FAILED;
42  struct sembuf sb = {0, 0, 0};
43  mode_t mode = 0;
44  int size;
45  int i, index, saved_errno;
46  char *real_name;
47  char *ptr;
48  struct mq_attr *user_attr = NULL;
49  struct libc_mq *info;
50  union semun arg;
51 
52  /* ignore opening slash if present */
53  if (*name == '/')
54    ++name; 
55  size = strlen(name);
56
57  if ((real_name = (char *)malloc (size + sizeof(MSGQ_PREFIX))) == NULL ||
58      (info = (struct libc_mq *)malloc (sizeof(struct libc_mq))) == NULL)
59    {
60      errno = ENOSPC;
61      if (real_name)
62        free (real_name);
63      return (mqd_t)-1;
64    }
65 
66  /* use given name to create shared memory file name - we convert any
67     slashes to underscores so we don't have to create directories */
68  memcpy (real_name, MSGQ_PREFIX, sizeof(MSGQ_PREFIX) - 1);
69  memcpy (real_name + sizeof(MSGQ_PREFIX) - 1, name, size + 1);
70  ptr = real_name + sizeof(MSGQ_PREFIX) - 1;
71  for (i = 0; i < size; ++i)
72    {
73      if (*ptr == '/')
74        *ptr = '_';
75      ++ptr;
76    }
77
78  /* open shared memory file based on msg queue open flags and then use memory
79     file to create a unique key to use for semaphores, etc.. */
80  if (oflag & O_CREAT)
81    {
82      va_list list;
83      va_start (list, oflag);
84
85      saved_errno = errno;
86      mode = (mode_t)va_arg (list, int);
87      user_attr = va_arg(list,struct mq_attr *);
88      va_end (list);
89
90      /* attempt to open the shared memory file for exclusive create so we know
91         whether we are the owners or not */
92      fd = open (real_name, O_RDWR | O_CREAT | O_EXCL, mode);
93      if (fd < 0 && (oflag & O_EXCL))
94        {
95          /* we failed and the user wanted exclusive create */
96          free (real_name);
97          free (info);
98          return (mqd_t)-1;
99        }
100      errno = saved_errno;
101      /* check if we created the file or not */
102      if (fd >= 0)
103        created = 1;
104    }
105         
106  if (fd < 0)
107    fd = open (real_name, O_RDWR, 0);
108
109  if (fd >= 0)
110    key = ftok(real_name, 255);
111
112  if (key != (key_t)-1)
113    /* memory map the shared memory file so we have a global shared data area to use */
114    attr = (struct mq_attr *)mmap (0, sizeof(struct mq_attr), PROT_READ | PROT_WRITE,
115                                   MAP_SHARED, fd, 0);
116 
117  if (attr != (struct mq_attr *)MAP_FAILED)
118    {
119      /* we need semaphores to prevent multi-process race conditions on the
120         shared storage which contains a shared structure.  The following
121         are the ones we need.
122         
123         0 = open semaphore
124         1 = number of opens
125         2 = number of writes left until queue is full
126         3 = number of reads available in queue
127         4 = notify semaphore
128         5 = number of readers */
129      arg.val = 0;
130      /* make sure the creator of the shared memory file also is the creator of the
131         semaphores...this will ensure that it also creates the message queue */
132      if (created)
133        {
134          saved_errno = errno;
135          semid = semget (key, 6, IPC_CREAT | IPC_EXCL | mode);
136          errno = saved_errno;
137          /* now that we have created the semaphore, we should initialize it */
138          if (semid != -1)
139            semctl (semid, 0, SETVAL, arg);
140        }
141      else
142        {
143          /* if we didn't create the shared memory file but have gotten to here, we want
144             to ensure we haven't gotten ahead of the creator temporarily so we will
145             loop until the semaphore exists.  This ensures that the creator will be the
146             one to create the message queue with the correct mode and we will be blocked
147             by the open semaphore 0.  We impose a time limit to ensure something terrible
148             hasn't gone wrong. */
149          struct timespec tms;
150          int i;
151
152          tms.tv_sec = 0;
153          tms.tv_nsec = 10000; /* 10 microseconds */
154          for (i = 0; i < 100; ++i)
155            {
156              if ((semid = semget (key, 6, 0)) != -1)
157                break;
158              /* sleep in case we our a higher priority process */
159              nanosleep (&tms, NULL);
160            }
161        }
162    }
163
164  if (semid != -1)
165    {
166      /* acquire main open semaphore if we didn't create it */
167      if (!created)
168        {
169          sb.sem_op = -1;
170          rc = semop (semid, &sb, 1);
171        }
172      else
173        rc = 0; /* need this to continue below */
174    }
175     
176  if (rc == 0)
177    {
178      if (created)
179        {
180          /* the creator must get here first so the message queue will be created */
181          msgqid = msgget (key, IPC_CREAT | mode); 
182          if (msgqid >= 0)
183            {
184              /* we have created the message queue so check and set the attributes */
185              if ((wrbuf = (MSG *)malloc (user_attr->mq_msgsize + sizeof(int))) == NULL ||
186                  (rdbuf = (MSG *)malloc (user_attr->mq_msgsize + sizeof(int))) == NULL ||
187                  user_attr == NULL || user_attr->mq_msgsize <= 0 || user_attr->mq_maxmsg <= 0)
188                {
189                  /* we're out of space and we created the message queue so we should
190                     try to remove it */
191                  msgctl (msgqid, IPC_RMID, NULL);
192                  msgqid = -1; /* allow clean up to occur below */
193                  if (wrbuf && rdbuf)
194                    errno = EINVAL;
195                  else
196                    errno = ENOSPC;
197                }
198              else /* valid attributes */
199                {
200                  write (fd, user_attr, sizeof(struct mq_attr));
201                  attr->mq_curmsgs = 0;
202                  attr->mq_flags = oflag & O_NONBLOCK;
203                  arg.val = 0;
204                  semctl (semid, 1, SETVAL, arg); /* number of opens starts at 0 */
205                  semctl (semid, 3, SETVAL, arg); /* number of reads available starts at 0 */
206                  semctl (semid, 5, SETVAL, arg); /* number of readers starts at 0 */
207                  arg.val = 1;
208                  semctl (semid, 4, SETVAL, arg); /* notify semaphore */
209                  arg.val = user_attr->mq_maxmsg;
210                  semctl (semid, 2, SETVAL, arg); /* number of writes left starts at mq_maxmsg */
211                }
212            }
213        }
214      else /* just open it */
215        {
216          msgqid = msgget (key, 0);
217          wrbuf = (MSG *)malloc (attr->mq_msgsize + sizeof(int));
218          rdbuf = (MSG *)malloc (attr->mq_msgsize + sizeof(int));
219        }
220     
221      /* release semaphore acquired earlier */
222      sb.sem_op = 1;
223      semop (semid, &sb, 1);
224    }
225
226  /* if we get here and we haven't got a message queue id, then we need to clean up
227     our mess and return failure */
228  if (msgqid < 0)
229    {
230      if (fd >= 0)
231        close (fd);
232      if (attr != (struct mq_attr *)MAP_FAILED)
233        munmap (attr, sizeof(struct mq_attr));
234      if (created)
235        {
236          unlink (real_name);
237          if (semid != -1)
238            semctl (semid, 0, IPC_RMID);
239        }
240      free (real_name);
241      free (info);
242      if (wrbuf)
243        free (wrbuf);
244      if (rdbuf)
245        free (rdbuf);
246      return (mqd_t)-1;
247    }
248
249  /* we are successful so register the message queue */
250
251  /* up the count of msg queue opens */
252  sb.sem_op = 1;
253  sb.sem_num = 1;
254  semop (semid, &sb, 1);
255
256  /* success, translate into index into mq_info array */ 
257  __lock_acquire(mq_hash_lock);
258  index = mq_index++;
259  info->index = index;
260  info->msgqid = msgqid;
261  info->name = real_name;
262  info->semid = semid;
263  info->fd = fd;
264  info->oflag = oflag;
265  info->wrbuf = wrbuf;
266  info->rdbuf = rdbuf;
267  info->cleanup_notify = NULL;
268  info->next = mq_hash[LOCHASH(index)];
269  info->attr = attr;
270  mq_hash[LOCHASH(index)] = info;
271  __lock_release(mq_hash_lock);
272
273  return (mqd_t)index;
274}
275
276struct libc_mq *
277__find_mq (mqd_t mq)
278{
279  struct libc_mq *ptr;
280
281  __lock_acquire(mq_hash_lock);
282
283  ptr = mq_hash[LOCHASH((int)mq)];
284
285  while (ptr)
286    {
287      if (ptr->index == (int)mq)
288        break;
289      ptr = ptr->next;
290    }
291
292  __lock_release(mq_hash_lock);
293
294  return ptr;
295}
296     
297void
298__cleanup_mq (mqd_t mq)
299{
300  struct libc_mq *ptr;
301  struct libc_mq *prev;
302  int semid;
303  struct sembuf sb = {0, 0, 0};
304
305  __lock_acquire(mq_hash_lock);
306
307  ptr = mq_hash[LOCHASH((int)mq)];
308  prev = NULL;
309
310  while (ptr)
311    {
312      if (ptr->index == (int)mq)
313        break;
314      prev = ptr;
315      ptr = ptr->next;
316    }
317
318  if (ptr != NULL)
319    {
320      if (ptr->cleanup_notify != NULL)
321        ptr->cleanup_notify (ptr);
322      if (prev != NULL)
323        prev->next = ptr->next;
324      else
325        mq_hash[LOCHASH((int)mq)] = NULL;
326      munmap (ptr->attr, sizeof(struct mq_attr));
327      close (ptr->fd);
328      free (ptr->name);
329      free (ptr->wrbuf);
330      free (ptr->rdbuf);
331      semid = ptr->semid;
332      free (ptr);
333      /* lower the count of msg queue opens */
334      sb.sem_op = -1;
335      sb.sem_num = 1;
336      sb.sem_flg = IPC_NOWAIT;
337      semop (semid, &sb, 1);
338    }
339
340  __lock_release(mq_hash_lock);
341}
342
343
344
345
346
Note: See TracBrowser for help on using the repository browser.