diff options
author | Jeff Johnston <jjohnstn@redhat.com> | 2002-06-19 22:17:33 +0000 |
---|---|---|
committer | Jeff Johnston <jjohnstn@redhat.com> | 2002-06-19 22:17:33 +0000 |
commit | ef467338e4a290b0f8978a0701b404e17c5bb048 (patch) | |
tree | 94570e7616b92bab0db3acef31bcf53dd6ce7a24 /newlib/libc/sys/linux/mq_open.c | |
parent | 676a64b87d76d8c0a518dbcb2e03d8ae4c59ced4 (diff) | |
download | cygnal-ef467338e4a290b0f8978a0701b404e17c5bb048.tar.gz cygnal-ef467338e4a290b0f8978a0701b404e17c5bb048.tar.bz2 cygnal-ef467338e4a290b0f8978a0701b404e17c5bb048.zip |
2002-06-19 Jeff Johnston <jjohnstn@redhat.com>
* libc/sys/linux/Makefile.am: Add support for message queue routines,
ipc routines, and ftok.
* libc/sys/linux/Makefile.in: Regenerated.
* libc/sys/linux/ftok.c: New file.
* libc/sys/linux/ipc.c: Ditto.
* libc/sys/linux/mq_close.c: Ditto.
* libc/sys/linux/mq_getattr.c: Ditto.
* libc/sys/linux/mq_notify.c: Ditto.
* libc/sys/linux/mq_open.c: Ditto.
* libc/sys/linux/mq_receive.c: Ditto.
* libc/sys/linux/mq_send.c: Ditto.
* libc/sys/linux/mq_setattr.c: Ditto.
* libc/sys/linux/mq_unlink.c: Ditto.
* libc/sys/linux/mqlocal.h: Ditto.
* libc/sys/linux/include/mqueue.h: Ditto.
* libc/sys/linux/sys/types.h: Define __gid_t_defined and
__uid_t_defined.
Diffstat (limited to 'newlib/libc/sys/linux/mq_open.c')
-rw-r--r-- | newlib/libc/sys/linux/mq_open.c | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/newlib/libc/sys/linux/mq_open.c b/newlib/libc/sys/linux/mq_open.c new file mode 100644 index 000000000..b8da2144d --- /dev/null +++ b/newlib/libc/sys/linux/mq_open.c @@ -0,0 +1,340 @@ +/* Copyright 2002, Red Hat Inc. */ + +#include <mqueue.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/ipc.h> +#include <sys/sem.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <stdarg.h> +#include <machine/weakalias.h> +#define _LIBC +#include <sys/lock.h> +#undef _LIBC + +#include "mqlocal.h" + +#define NHASH 32 /* Num of hash lists, must be a power of 2 */ +#define LOCHASH(i) ((i)&(NHASH-1)) + +static long mq_index; /* Index of next entry */ +static struct libc_mq *mq_hash[NHASH]; /* Hash list heads for mqopen_infos */ + +__LOCK_INIT(static, mq_hash_lock); + +mqd_t +mq_open (const char *name, int oflag, ...) +{ + MSG *wrbuf = NULL; + MSG *rdbuf = NULL; + int msgqid = -1; + int rc = -1; + int fd = -1; + int semid = -1; + int created = 0; + key_t key = (key_t)-1; + struct mq_attr *attr = (struct mq_attr *)MAP_FAILED; + struct sembuf sb = {0, 0, 0}; + mode_t mode = 0; + int size; + int i, index, saved_errno; + char *real_name; + char *ptr; + struct mq_attr *user_attr = NULL; + struct libc_mq *info; + union semun arg; + + /* ignore opening slash if present */ + if (*name == '/') + ++name; + size = strlen(name); + + if ((real_name = (char *)malloc (size + sizeof(MSGQ_PREFIX))) == NULL || + (info = (struct libc_mq *)malloc (sizeof(struct libc_mq))) == NULL) + { + errno = ENOSPC; + if (real_name) + free (real_name); + return (mqd_t)-1; + } + + /* use given name to create shared memory file name - we convert any + slashes to underscores so we don't have to create directories */ + memcpy (real_name, MSGQ_PREFIX, sizeof(MSGQ_PREFIX) - 1); + memcpy (real_name + sizeof(MSGQ_PREFIX) - 1, name, size + 1); + ptr = real_name + sizeof(MSGQ_PREFIX) - 1; + for (i = 0; i < size; ++i) + { + if (*ptr == '/') + *ptr = '_'; + ++ptr; + } + + /* open shared memory file based on msg queue open flags and then use memory + file to create a unique key to use for semaphores, etc.. */ + if (oflag & O_CREAT) + { + va_list list; + va_start (list, oflag); + + saved_errno = errno; + mode = (mode_t)va_arg (list, int); + user_attr = va_arg(list,struct mq_attr *); + va_end (list); + + /* attempt to open the shared memory file for exclusive create so we know + whether we are the owners or not */ + fd = open (real_name, O_RDWR | O_CREAT | O_EXCL, mode); + if (fd < 0 && (oflag & O_EXCL)) + { + /* we failed and the user wanted exclusive create */ + free (real_name); + free (info); + return (mqd_t)-1; + } + errno = saved_errno; + created = 1; + } + + if (fd < 0) + fd = open (real_name, O_RDWR, 0); + + if (fd >= 0) + key = ftok(real_name, 255); + + if (key != (key_t)-1) + /* memory map the shared memory file so we have a global shared data area to use */ + attr = (struct mq_attr *)mmap (0, sizeof(struct mq_attr), PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + + if (attr != (struct mq_attr *)MAP_FAILED) + { + /* we need semaphores to prevent multi-process race conditions on the + shared storage which contains a shared structure. The following + are the ones we need. + + 0 = open semaphore + 1 = number of opens + 2 = number of writes left until queue is full + 3 = number of reads available in queue + 4 = notify semaphore + 5 = number of readers */ + arg.val = 0; + /* make sure the creator of the shared memory file also is the creator of the + semaphores...this will ensure that it also creates the message queue */ + if (created) + { + saved_errno = errno; + semid = semget (key, 6, IPC_CREAT | IPC_EXCL | mode); + errno = saved_errno; + /* now that we have created the semaphore, we should initialize it */ + if (semid != -1) + semctl (semid, 0, SETVAL, arg); + } + else + { + /* if we didn't create the shared memory file but have gotten to here, we want + to ensure we haven't gotten ahead of the creator temporarily so we will + loop until the semaphore exists. This ensures that the creator will be the + one to create the message queue with the correct mode and we will be blocked + by the open semaphore 0. We impose a time limit to ensure something terrible + hasn't gone wrong. */ + struct timespec tms; + int i; + + tms.tv_sec = 0; + tms.tv_nsec = 10000; /* 10 microseconds */ + for (i = 0; i < 100; ++i) + { + if ((semid = semget (key, 6, 0)) != -1) + break; + /* sleep in case we our a higher priority process */ + nanosleep (&tms, NULL); + } + } + } + + if (semid != -1) + { + /* acquire main open semaphore if we didn't create it */ + if (!created) + { + sb.sem_op = -1; + rc = semop (semid, &sb, 1); + } + else + rc = 0; /* need this to continue below */ + } + + if (rc == 0) + { + if (created) + { + /* the creator must get here first so the message queue will be created */ + msgqid = msgget (key, IPC_CREAT | mode); + if (msgqid >= 0) + { + /* we have created the message queue so check and set the attributes */ + if ((wrbuf = (MSG *)malloc (user_attr->mq_msgsize + sizeof(int))) == NULL || + (rdbuf = (MSG *)malloc (user_attr->mq_msgsize + sizeof(int))) == NULL || + user_attr == NULL || user_attr->mq_msgsize <= 0 || user_attr->mq_maxmsg <= 0) + { + /* we're out of space and we created the message queue so we should + try to remove it */ + msgctl (msgqid, IPC_RMID, NULL); + msgqid = -1; /* allow clean up to occur below */ + if (wrbuf && rdbuf) + errno = EINVAL; + else + errno = ENOSPC; + } + else /* valid attributes */ + { + write (fd, user_attr, sizeof(struct mq_attr)); + attr->mq_curmsgs = 0; + attr->mq_flags = oflag & O_NONBLOCK; + arg.val = 0; + semctl (semid, 1, SETVAL, arg); /* number of opens starts at 0 */ + semctl (semid, 3, SETVAL, arg); /* number of reads available starts at 0 */ + semctl (semid, 5, SETVAL, arg); /* number of readers starts at 0 */ + arg.val = 1; + semctl (semid, 4, SETVAL, arg); /* notify semaphore */ + arg.val = user_attr->mq_maxmsg; + semctl (semid, 2, SETVAL, arg); /* number of writes left starts at mq_maxmsg */ + } + } + } + else /* just open it */ + msgqid = msgget (key, 0); + + /* release semaphore acquired earlier */ + sb.sem_op = 1; + semop (semid, &sb, 1); + } + + /* if we get here and we haven't got a message queue id, then we need to clean up + our mess and return failure */ + if (msgqid < 0) + { + if (fd >= 0) + close (fd); + if (attr != (struct mq_attr *)MAP_FAILED) + munmap (attr, sizeof(struct mq_attr)); + if (created) + { + unlink (real_name); + if (semid != -1) + semctl (semid, 0, IPC_RMID); + } + free (real_name); + free (info); + if (wrbuf) + free (wrbuf); + if (rdbuf) + free (rdbuf); + return (mqd_t)-1; + } + + /* we are successful so register the message queue */ + + /* up the count of msg queue opens */ + sb.sem_op = 1; + sb.sem_num = 1; + semop (semid, &sb, 1); + + /* success, translate into index into mq_info array */ + __lock_acquire(mq_hash_lock); + index = mq_index++; + info->index = index; + info->msgqid = msgqid; + info->name = real_name; + info->semid = semid; + info->fd = fd; + info->oflag = oflag; + info->wrbuf = wrbuf; + info->rdbuf = rdbuf; + info->cleanup_notify = NULL; + info->next = mq_hash[LOCHASH(index)]; + info->attr = attr; + mq_hash[LOCHASH(index)] = info; + __lock_release(mq_hash_lock); + + return (mqd_t)index; +} + +struct libc_mq * +__find_mq (mqd_t mq) +{ + struct libc_mq *ptr; + + __lock_acquire(mq_hash_lock); + + ptr = mq_hash[LOCHASH((int)mq)]; + + while (ptr) + { + if (ptr->index == (int)mq) + break; + ptr = ptr->next; + } + + __lock_release(mq_hash_lock); + + return ptr; +} + +void +__cleanup_mq (mqd_t mq) +{ + struct libc_mq *ptr; + struct libc_mq *prev; + int semid; + struct sembuf sb = {0, 0, 0}; + + __lock_acquire(mq_hash_lock); + + ptr = mq_hash[LOCHASH((int)mq)]; + prev = NULL; + + while (ptr) + { + if (ptr->index == (int)mq) + break; + prev = ptr; + ptr = ptr->next; + } + + if (ptr != NULL) + { + if (ptr->cleanup_notify != NULL) + ptr->cleanup_notify (ptr); + if (prev != NULL) + prev->next = ptr->next; + else + mq_hash[LOCHASH((int)mq)] = NULL; + munmap (ptr->attr, sizeof(struct mq_attr)); + close (ptr->fd); + free (ptr->name); + free (ptr->wrbuf); + free (ptr->rdbuf); + semid = ptr->semid; + free (ptr); + /* lower the count of msg queue opens */ + sb.sem_op = -1; + sb.sem_num = 1; + sb.sem_flg = IPC_NOWAIT; + semop (semid, &sb, 1); + } + + __lock_release(mq_hash_lock); +} + + + + + |