From 50a6ac7fb90ad4008b354ff8e72c6ce511dbeb3a Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Tue, 11 Jan 2005 09:41:40 +0000 Subject: Patch from Paul Mundt (lethal) adding an initial librt implementation. I then reworked the syscall handling and made minor cleanups. With luck I've not completely broken his patch... --- Makefile | 3 +- include/mqueue.h | 97 +++++++++++++++++++++++++++++++++++++++++++++ librt/Makefile | 47 ++++++++++++++++++++++ librt/kernel-posix-timers.h | 30 ++++++++++++++ librt/mq_close.c | 22 ++++++++++ librt/mq_getsetattr.c | 33 +++++++++++++++ librt/mq_notify.c | 28 +++++++++++++ librt/mq_open.c | 52 ++++++++++++++++++++++++ librt/mq_receive.c | 36 +++++++++++++++++ librt/mq_send.c | 36 +++++++++++++++++ librt/mq_unlink.c | 38 ++++++++++++++++++ librt/timer_create.c | 70 ++++++++++++++++++++++++++++++++ librt/timer_delete.c | 33 +++++++++++++++ librt/timer_getoverr.c | 25 ++++++++++++ librt/timer_gettime.c | 26 ++++++++++++ librt/timer_settime.c | 28 +++++++++++++ 16 files changed, 603 insertions(+), 1 deletion(-) create mode 100644 include/mqueue.h create mode 100644 librt/Makefile create mode 100644 librt/kernel-posix-timers.h create mode 100644 librt/mq_close.c create mode 100644 librt/mq_getsetattr.c create mode 100644 librt/mq_notify.c create mode 100644 librt/mq_open.c create mode 100644 librt/mq_receive.c create mode 100644 librt/mq_send.c create mode 100644 librt/mq_unlink.c create mode 100644 librt/timer_create.c create mode 100644 librt/timer_delete.c create mode 100644 librt/timer_getoverr.c create mode 100644 librt/timer_gettime.c create mode 100644 librt/timer_settime.c diff --git a/Makefile b/Makefile index 9fac2d363..ec2c462cd 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ noconfig_targets := menuconfig config oldconfig randconfig \ TOPDIR=./ include Rules.mak -DIRS = ldso libc libcrypt libresolv libnsl libutil libm libpthread +DIRS = ldso libc libcrypt libresolv libnsl libutil libm libpthread librt ifeq ($(strip $(UCLIBC_HAS_GETTEXT_AWARENESS)),y) DIRS += libintl endif @@ -50,6 +50,7 @@ ifeq ($(strip $(HAVE_SHARED)),y) @$(MAKE) -C libutil shared @$(MAKE) -C libm shared @$(MAKE) -C libpthread shared + @$(MAKE) -C librt shared ifeq ($(strip $(UCLIBC_HAS_GETTEXT_AWARENESS)),y) @$(MAKE) -C libintl shared endif diff --git a/include/mqueue.h b/include/mqueue.h new file mode 100644 index 000000000..7f978ba34 --- /dev/null +++ b/include/mqueue.h @@ -0,0 +1,97 @@ +/* + * mqueue.h - definitions and function prototypes for POSIX mqueue support. + */ + +#ifndef _MQUEUE_H +#define _MQUEUE_H + +#include +#include +#include +#define __need_sigevent_t +#include +#define __need_timespec +#include + +typedef int mqd_t; + +struct mq_attr { + long int mq_flags; /* Message queue flags */ + long int mq_maxmsg; /* Maximum number of messages */ + long int mq_msgsize; /* Maximum message size */ + long int mq_curmsgs; /* Number of messages currently queued */ + long int __pad[4]; +}; + +__BEGIN_DECLS + +/* + * Establish connection between a process and a message queue __name and + * return message queue descriptor or (mqd_t) -1 on error. __oflag determines + * the type of access used. If O_CREAT is on __oflag, the third argument is + * taken as a `mode_t', the mode of the created message queue, and the fourth + * argument is taken as `struct mq_attr *', pointer to message queue + * attributes. If the fourth argument is NULL, default attributes are used. + */ +extern mqd_t mq_open(const char *__name, int __oflag, ...) __THROW; + +/* + * Remove the association between message queue descriptor __mqdes and its + * message queue. + */ +extern int mq_close(mqd_t __mqdes) __THROW; + +/* Query status and attributes of message queue __mqdes */ +extern int mq_getattr(mqd_t __mqdes, struct mq_attr *__mqstat) __THROW; + +/* + * Set attributes associated with message queue __mqdes and if __omqstat is + * not NULL also query its old attributes. + */ +extern int mq_setattr(mqd_t __mqdes, + const struct mq_attr *__restrict __mqstat, + struct mq_attr *__restrict __omqstat) __THROW; + +/* Remove message queue named __name */ +extern int mq_unlink(const char *__name) __THROW; + +/* + * Register notification upon message arrival to an empty message queue + * __mqdes + */ +extern int mq_notify(mqd_t __mqdes, const struct sigevent *__notification) + __THROW; + +/* + * Receive the oldest from highest priority messages in message queue + * __mqdes + */ +extern ssize_t mq_receive(mqd_t __mqdes, char *__msg_ptr, size_t __msg_len, + unsigned int *__msg_prio); + +/* Add message pointed by __msg_ptr to message queue __mqdes */ +extern int mq_send(mqd_t __mqdes, const char *__msg_ptr, size_t __msg_len, + unsigned int __msg_prio); + +#ifdef __USE_XOPEN2K +/* + * Receive the oldest from highest priority messages in message queue + * __mqdes, stop waiting if __abs_timeout expires. + */ +extern ssize_t mq_timedreceive(mqd_t __mqdes, char *__restrict __msg_ptr, + size_t __msg_len, + unsigned int *__restrict __msg_prio, + const struct timespec *__restrict __abs_timeout); + +/* + * Add message pointed by __msg_ptr to message queue __mqdes, stop blocking + * on full message queue if __abs_timeout expires. + */ +extern int mq_timedsend(mqd_t __mqdes, const char *__msg_ptr, + size_t __msg_len, unsigned int __msg_prio, + const struct timespec *__abs_timeout); +#endif + +__END_DECLS + +#endif diff --git a/librt/Makefile b/librt/Makefile new file mode 100644 index 000000000..b84520af3 --- /dev/null +++ b/librt/Makefile @@ -0,0 +1,47 @@ +# +# Makefile for librt +# + +TOPDIR=../ +include $(TOPDIR)Rules.mak +LIBC=$(TOPDIR)libc.a + +LIBRT=librt.a +LIBRT_SHARED=librt.so +LIBRT_SHARED_FULLNAME=librt-$(MAJOR_VERSION).$(MINOR_VERSION).$(SUBLEVEL).so + +# uClibc's librt lacks all aio routines, all clock routines, +# and all shm routines +CSRC=mq_open.c mq_close.c mq_unlink.c mq_getsetattr.c \ + mq_send.c mq_receive.c mq_notify.c \ + timer_create.c timer_delete.c \ + timer_settime.c timer_gettime.c timer_getoverr.c +OBJS=$(patsubst %.c,%.o, $(CSRC)) + +all: $(OBJS) $(LIBC) + +$(LIBC): ar-target + +ar-target: $(OBJS) + $(AR) $(ARFLAGS) $(LIBRT) $(OBJS) + $(INSTALL) -d $(TOPDIR)lib + $(RM) $(TOPDIR)lib/$(LIBRT) + $(INSTALL) -m 644 $(LIBRT) $(TOPDIR)lib/ + +$(OBJS): %.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + $(STRIPTOOL) -x -R .note -R .comment $*.o + +shared: all + $(LD) $(LDFLAGS) -soname=$(LIBRT_SHARED).$(MAJOR_VERSION) \ + -o $(LIBRT_SHARED_FULLNAME) --whole-archive $(LIBRT) \ + --no-whole-archive $(TOPDIR)libc/misc/internals/interp.o \ + -L$(TOPDIR)lib -lc $(LDADD_LIBFLOAT) $(LIBGCC); + $(INSTALL) -d $(TOPDIR)lib + $(RM) $(TOPDIR)lib/$(LIBRT_SHARED_FULLNAME) $(TOPDIR)lib/$(LIBRT_SHARED).$(MAJOR_VERSION) + $(INSTALL) -m 644 $(LIBRT_SHARED_FULLNAME) $(TOPDIR)lib + $(LN) -sf $(LIBRT_SHARED_FULLNAME) $(TOPDIR)lib/$(LIBRT_SHARED) + $(LN) -sf $(LIBRT_SHARED_FULLNAME) $(TOPDIR)lib/$(LIBRT_SHARED).$(MAJOR_VERSION) + +clean: + $(RM) *.[oa] *~ core $(LIBRT_SHARED)* $(LIBRT_SHARED_FULLNAME)* diff --git a/librt/kernel-posix-timers.h b/librt/kernel-posix-timers.h new file mode 100644 index 000000000..9a538c715 --- /dev/null +++ b/librt/kernel-posix-timers.h @@ -0,0 +1,30 @@ +/* + * kernel-posix-timers.h - kernel-dependent definitions for POSIX timers. + */ + +#include +#include +#include + +/* Type of timers in the kernel */ +typedef int kernel_timer_t; + +/* Internal representation of timer */ +struct timer { + /* Notification mechanism */ + int sigev_notify; + + /* Timer ID returned by the kernel */ + kernel_timer_t ktimerid; + + /* + * All new elements must be added after ktimerid. And if the thrfunc + * element is not the third element anymore the memory allocation in + * timer_create needs to be changed. + */ + + /* Parameters for the thread to be started for SIGEV_THREAD */ + void (*thrfunc) (sigval_t); + sigval_t sival; + pthread_attr_t attr; +}; diff --git a/librt/mq_close.c b/librt/mq_close.c new file mode 100644 index 000000000..055ad1fec --- /dev/null +++ b/librt/mq_close.c @@ -0,0 +1,22 @@ +/* + * mq_close.c - close a message queue. + */ + +#include +#include +#include + +#include + +#ifdef __NR_mq_open + +/* + * Remove the association between message queue descriptor and its + * message queue. + */ +int mq_close(mqd_t mqdes) +{ + return close(mqdes); +} + +#endif diff --git a/librt/mq_getsetattr.c b/librt/mq_getsetattr.c new file mode 100644 index 000000000..4ffda26ca --- /dev/null +++ b/librt/mq_getsetattr.c @@ -0,0 +1,33 @@ +/* + * mq_getattr.c - get message queue attributes. + */ + +#include +#include +#include + +#include + +#ifdef __NR_mq_getsetattr + +#define __NR___syscall_mq_getsetattr __NR_mq_getsetattr +static inline _syscall3(int, __syscall_mq_getsetattr, int, mqdes, + const void *, mqstat, void *, omqstat); + +/* + * Set attributes associated with message queue (and possibly also get + * its old attributes) + */ +int mq_setattr(mqd_t mqdes, const struct mq_attr *mqstat, + struct mq_attr *omqstat) +{ + return __syscall_mq_getsetattr(mqdes, mqstat, omqstat); +} + +/* Query status and attributes of message queue */ +int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat) +{ + return mq_setattr(mqdes, NULL, mqstat); +} + +#endif diff --git a/librt/mq_notify.c b/librt/mq_notify.c new file mode 100644 index 000000000..4e7953242 --- /dev/null +++ b/librt/mq_notify.c @@ -0,0 +1,28 @@ +/* + * mq_notify.c - notify process that a message is available. + */ + +#include +#include +#include + +#include + +#ifdef __NR_mq_notify + +#define __NR___syscall_mq_notify __NR_mq_notify +static inline _syscall2(int, __syscall_mq_notify, int, mqdes, + const void *, notification); + +/* Register notification upon message arrival to an empty message queue */ +int mq_notify(mqd_t mqdes, const struct sigevent *notification) +{ + /* We don't support SIGEV_THREAD notification yet */ + if (notification != NULL && notification->sigev_notify == SIGEV_THREAD) { + __set_errno(ENOSYS); + return -1; + } + return __syscall_mq_notify(mqdes, notification); +} + +#endif diff --git a/librt/mq_open.c b/librt/mq_open.c new file mode 100644 index 000000000..3648b5aa7 --- /dev/null +++ b/librt/mq_open.c @@ -0,0 +1,52 @@ +/* + * mq_open.c - open a message queue. + */ + +#include +#include +#include +#include + +#include + +#ifdef __NR_mq_open + +#define __NR___syscall_mq_open __NR_mq_open +static inline _syscall4(int, __syscall_mq_open, const char *, name, + int, oflag, __kernel_mode_t, mode, void *, attr); +/* + * Establish connection between a process and a message queue and + * return message queue descriptor or (mqd_t) -1 on error. + * oflag determines the type of access used. If O_CREAT is on oflag, the + * third argument is taken as a `mode_t', the mode of the created + * message queue, and the fourth argument is taken as `struct mq_attr *', + * pointer to message queue attributes. + * If the fourth argument is NULL, default attributes are used. + */ +mqd_t mq_open(const char *name, int oflag, ...) +{ + mode_t mode; + struct mq_attr *attr; + + if (name[0] != '/') { + __set_errno(EINVAL); + return -1; + } + + mode = 0; + attr = NULL; + + if (oflag & O_CREAT) { + va_list ap; + + va_start(ap, oflag); + mode = va_arg(ap, mode_t); + attr = va_arg(ap, struct mq_attr *); + + va_end(ap); + } + + return __syscall_mq_open(name + 1, oflag, mode, attr); +} + +#endif diff --git a/librt/mq_receive.c b/librt/mq_receive.c new file mode 100644 index 000000000..4dd81f5e7 --- /dev/null +++ b/librt/mq_receive.c @@ -0,0 +1,36 @@ +/* + * mq_receive.c - functions for receiving from message queue. + */ + +#include +#include +#include + +#include + +#ifdef __NR_mq_timedreceive + +#define __NR___syscall_mq_timedreceive __NR_mq_timedreceive +static inline _syscall5(int, __syscall_mq_timedreceive, int, mqdes, + char *, msg_ptr, size_t, msg_len, unsigned int *, msg_prio, + const void *, abs_timeout); + +/* + * Receive the oldest from highest priority messages. + * Stop waiting if abs_timeout expires. + */ +ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len, + unsigned int *msg_prio, + const struct timespec *abs_timeout) +{ + return __syscall_mq_timedreceive(mqdes, msg_ptr, msg_len, msg_prio, abs_timeout); +} + +/* Receive the oldest from highest priority messages */ +ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, + unsigned int *msg_prio) +{ + return mq_timedreceive(mqdes, msg_ptr, msg_len, msg_prio, NULL); +} + +#endif diff --git a/librt/mq_send.c b/librt/mq_send.c new file mode 100644 index 000000000..947e9fe72 --- /dev/null +++ b/librt/mq_send.c @@ -0,0 +1,36 @@ +/* + * mq_send.c - functions for sending to message queue. + */ + +#include +#include +#include + +#include + +#ifdef __NR_mq_timedsend + +#define __NR___syscall_mq_timedsend __NR_mq_timedsend +static inline _syscall5(int, __syscall_mq_timedsend, int, mqdes, + const char *, msg_ptr, size_t, msg_len, unsigned int, msg_prio, + const void *, abs_timeout); + +/* + * Add a message to queue. If O_NONBLOCK is set and queue is full, wait + * for sufficient room in the queue until abs_timeout expires. + */ +int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len, + unsigned int msg_prio, + const struct timespec *abs_timeout) +{ + return __syscall_mq_timedsend(mqdes, msg_ptr, msg_len, msg_prio, abs_timeout); +} + +/* Add a message to queue */ +int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, + unsigned int msg_prio) +{ + return mq_timedsend(mqdes, msg_ptr, msg_len, msg_prio, NULL); +} + +#endif diff --git a/librt/mq_unlink.c b/librt/mq_unlink.c new file mode 100644 index 000000000..aee3478e7 --- /dev/null +++ b/librt/mq_unlink.c @@ -0,0 +1,38 @@ +/* + * mq_unlink.c - remove a message queue. + */ + +#include +#include + +#include + +#ifdef __NR_mq_unlink + +#define __NR___syscall_mq_unlink __NR_mq_unlink +static inline _syscall1(int, __syscall_mq_unlink, const char *, name); + +/* Remove message queue */ +int mq_unlink(const char *name) +{ + int ret; + if (name[0] != '/') { + __set_errno(EINVAL); + return -1; + } + + ret = __syscall_mq_unlink(name + 1); + + /* While unlink can return either EPERM or EACCES, mq_unlink should return just EACCES. */ + if (ret < 0) { + ret = errno; + if (ret == EPERM) + ret = EACCES; + __set_errno(ret); + ret = -1; + } + + return ret; +} + +#endif diff --git a/librt/timer_create.c b/librt/timer_create.c new file mode 100644 index 000000000..a49572792 --- /dev/null +++ b/librt/timer_create.c @@ -0,0 +1,70 @@ +/* + * timer_create.c - create a per-process timer. + */ + +#include +#include +#include +#include +#include +#include + +#include "kernel-posix-timers.h" + +#ifdef __NR_timer_create + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#define __NR___syscall_timer_create __NR_timer_create +static inline _syscall3(int, __syscall_timer_create, clockid_t, clock_id, + struct sigevent *, evp, kernel_timer_t *, ktimerid); + +/* Create a per-process timer */ +int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid) +{ + int retval; + kernel_timer_t ktimerid; + struct sigevent local_evp; + struct timer *newp; + + /* Notification via a thread is not supported yet */ + if (__builtin_expect(evp->sigev_notify == SIGEV_THREAD, 1)) + return -1; + + /* + * We avoid allocating too much memory by basically using + * struct timer as a derived class with the first two elements + * being in the superclass. We only need these two elements here. + */ + newp = (struct timer *) malloc(offsetof(struct timer, thrfunc)); + if (newp == NULL) + return -1; /* No memory */ + + if (evp == NULL) { + /* + * The kernel has to pass up the timer ID which is a userlevel object. + * Therefore we cannot leave it up to the kernel to determine it. + */ + local_evp.sigev_notify = SIGEV_SIGNAL; + local_evp.sigev_signo = SIGALRM; + local_evp.sigev_value.sival_ptr = newp; + + evp = &local_evp; + } + + retval = __syscall_timer_create(clock_id, evp, &ktimerid); + if (retval != -1) { + newp->sigev_notify = (evp != NULL ? evp->sigev_notify : SIGEV_SIGNAL); + newp->ktimerid = ktimerid; + + *timerid = (timer_t) newp; + } else { + /* Cannot allocate the timer, fail */ + free(newp); + retval = -1; + } + + return retval; +} + +#endif diff --git a/librt/timer_delete.c b/librt/timer_delete.c new file mode 100644 index 000000000..a85a51d9f --- /dev/null +++ b/librt/timer_delete.c @@ -0,0 +1,33 @@ +/* + * timer_delete.c - delete a per-process timer. + */ + +#include +#include +#include +#include + +#include "kernel-posix-timers.h" + +#ifdef __NR_timer_delete + +#define __NR___syscall_timer_delete __NR_timer_delete +static inline _syscall1(int, __syscall_timer_delete, kernel_timer_t, ktimerid); + +/* Delete a per-process timer */ +int timer_delete(timer_t timerid) +{ + int res; + struct timer *kt = (struct timer *) timerid; + + /* Delete the kernel timer object */ + res = __syscall_timer_delete(kt->ktimerid); + if (res == 0) { + free(kt); /* Free the memory */ + return 0; + } + + return -1; +} + +#endif diff --git a/librt/timer_getoverr.c b/librt/timer_getoverr.c new file mode 100644 index 000000000..7bc483d0f --- /dev/null +++ b/librt/timer_getoverr.c @@ -0,0 +1,25 @@ +/* + * timer-getoverr.c - get the timer overrun count. + */ + +#include +#include +#include + +#include "kernel-posix-timers.h" + +#ifdef __NR_timer_getoverrun + +#define __NR___syscall_timer_getoverrun __NR_timer_getoverrun +static inline _syscall1(int, __syscall_timer_getoverrun, kernel_timer_t, ktimerid); + +/* Get the timer overrun count */ +int timer_getoverrun(timer_t timerid) +{ + struct timer *kt = (struct timer *) timerid; + + /* Get the information from the kernel */ + return __syscall_timer_getoverrun(kt->ktimerid); +} + +#endif diff --git a/librt/timer_gettime.c b/librt/timer_gettime.c new file mode 100644 index 000000000..32e35eb05 --- /dev/null +++ b/librt/timer_gettime.c @@ -0,0 +1,26 @@ +/* + * timer_gettime.c - get the timer value. + */ + +#include +#include +#include +#include + +#include "kernel-posix-timers.h" + +#ifdef __NR_timer_gettime + +#define __NR___syscall_timer_gettime __NR_timer_gettime +static inline _syscall2(int, __syscall_timer_gettime, kernel_timer_t, ktimerid, void *, value); + +/* Get the amount of time left on a timer */ +int timer_gettime(timer_t timerid, struct itimerspec *value) +{ + struct timer *kt = (struct timer *) timerid; + + /* Get timeout from the kernel */ + return __syscall_timer_gettime(kt->ktimerid, value); +} + +#endif diff --git a/librt/timer_settime.c b/librt/timer_settime.c new file mode 100644 index 000000000..1a042283c --- /dev/null +++ b/librt/timer_settime.c @@ -0,0 +1,28 @@ +/* + * timer_settime.c - set the timer. + */ + +#include +#include +#include +#include + +#include "kernel-posix-timers.h" + +#ifdef __NR_timer_settime + +#define __NR___syscall_timer_settime __NR_timer_settime +static inline _syscall4(int, __syscall_timer_settime, kernel_timer_t, ktimerid, + int, flags, const void *, value, void *, ovalue); + +/* Set the expiration time for a timer */ +int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, + struct itimerspec *ovalue) +{ + struct timer *kt = (struct timer *) timerid; + + /* Set timeout */ + return __syscall_timer_settime(kt->ktimerid, flags, value, ovalue); +} + +#endif -- cgit v1.2.3