Browse Source

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...

Eric Andersen 19 years ago
parent
commit
50a6ac7fb9

+ 2 - 1
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

+ 97 - 0
include/mqueue.h

@@ -0,0 +1,97 @@
+/*
+ * mqueue.h - definitions and function prototypes for POSIX mqueue support.
+ */
+
+#ifndef _MQUEUE_H
+#define _MQUEUE_H
+
+#include <features.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#define __need_sigevent_t
+#include <bits/siginfo.h>
+#define __need_timespec
+#include <time.h>
+
+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

+ 47 - 0
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)*

+ 30 - 0
librt/kernel-posix-timers.h

@@ -0,0 +1,30 @@
+/*
+ * kernel-posix-timers.h - kernel-dependent definitions for POSIX timers.
+ */
+
+#include <setjmp.h>
+#include <signal.h>
+#include <sys/types.h>
+
+/* 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;
+};

+ 22 - 0
librt/mq_close.c

@@ -0,0 +1,22 @@
+/*
+ * mq_close.c - close a message queue.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#include <mqueue.h>
+
+#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

+ 33 - 0
librt/mq_getsetattr.c

@@ -0,0 +1,33 @@
+/*
+ * mq_getattr.c - get message queue attributes.
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <sys/syscall.h>
+
+#include <mqueue.h>
+
+#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

+ 28 - 0
librt/mq_notify.c

@@ -0,0 +1,28 @@
+/*
+ * mq_notify.c - notify process that a message is available.
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <sys/syscall.h>
+
+#include <mqueue.h>
+
+#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

+ 52 - 0
librt/mq_open.c

@@ -0,0 +1,52 @@
+/*
+ * mq_open.c - open a message queue.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <sys/syscall.h>
+
+#include <mqueue.h>
+
+#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

+ 36 - 0
librt/mq_receive.c

@@ -0,0 +1,36 @@
+/*
+ * mq_receive.c - functions for receiving from message queue.
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <sys/syscall.h>
+
+#include <mqueue.h>
+
+#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

+ 36 - 0
librt/mq_send.c

@@ -0,0 +1,36 @@
+/*
+ * mq_send.c - functions for sending to message queue.
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <sys/syscall.h>
+
+#include <mqueue.h>
+
+#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

+ 38 - 0
librt/mq_unlink.c

@@ -0,0 +1,38 @@
+/*
+ * mq_unlink.c - remove a message queue.
+ */
+
+#include <errno.h>
+#include <sys/syscall.h>
+
+#include <mqueue.h>
+
+#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

+ 70 - 0
librt/timer_create.c

@@ -0,0 +1,70 @@
+/*
+ * timer_create.c - create a per-process timer.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/syscall.h>
+
+#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

+ 33 - 0
librt/timer_delete.c

@@ -0,0 +1,33 @@
+/*
+ * timer_delete.c - delete a per-process timer.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/syscall.h>
+
+#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

+ 25 - 0
librt/timer_getoverr.c

@@ -0,0 +1,25 @@
+/*
+ * timer-getoverr.c - get the timer overrun count.
+ */
+
+#include <errno.h>
+#include <time.h>
+#include <sys/syscall.h>
+
+#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

+ 26 - 0
librt/timer_gettime.c

@@ -0,0 +1,26 @@
+/*
+ * timer_gettime.c - get the timer value.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/syscall.h>
+
+#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

+ 28 - 0
librt/timer_settime.c

@@ -0,0 +1,28 @@
+/*
+ * timer_settime.c - set the timer.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/syscall.h>
+
+#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