diff --git a/src/liblwevent/000_TopHeader.h b/src/liblwevent/000_TopHeader.h index baca26e..89b28fd 100644 --- a/src/liblwevent/000_TopHeader.h +++ b/src/liblwevent/000_TopHeader.h @@ -9,6 +9,8 @@ #define HEADER_liblwevent /* standard includes, or includes needed for type declarations */ +#include /* for 400_signalfd.h */ +#include /* for 400_signalfd.h */ /* options for text editors kate: replace-trailing-space-save true; space-indent true; tab-width 4; diff --git a/src/liblwevent/000_TopSource.c b/src/liblwevent/000_TopSource.c index 63e6d9b..089db86 100644 --- a/src/liblwevent/000_TopSource.c +++ b/src/liblwevent/000_TopSource.c @@ -13,6 +13,7 @@ #include #include #include +#include /* for 400_signalfd.c */ /* options for text editors kate: replace-trailing-space-save true; space-indent true; tab-width 4; diff --git a/src/liblwevent/300_fdevent.c b/src/liblwevent/300_fdevent.c index 8418b70..5b8942a 100644 --- a/src/liblwevent/300_fdevent.c +++ b/src/liblwevent/300_fdevent.c @@ -8,17 +8,18 @@ /* _lwevent_activate() - * Activates the event `ev'. Called when creating a new event in lwevent_new() or when reactivating - * an event in lwevent_reactivate(). Calls epoll_ctl(EPOLL_CTL_ADD). + * Activates the event `ev'. Called with EPOLL_CTL_ADD in `op' when creating a new event in + * lwevent_new() or when reactivating an event in lwevent_reactivate(). Called with EPOLL_CTL_MOD + * in lwevent_set_events(). Calls epoll_ctl(), returning -1 on permanent failure. */ -static int _lwevent_activate(struct lwevent* ev) +static int _lwevent_activate(int op, struct lwevent* ev) { struct epoll_event ee; ee.events = ev->events; ee.data.ptr = ev; - return TEMP_FAILURE_RETRY( epoll_ctl(lwevent_epoll_fd, EPOLL_CTL_ADD, ev->fd, &ee) ); + return TEMP_FAILURE_RETRY( epoll_ctl(lwevent_epoll_fd, op, ev->fd, &ee) ); } @@ -38,7 +39,7 @@ struct lwevent* lwevent_new(int fd, int events, lwevent_callback callback) ev->events = events; ev->callback = callback; - if(_lwevent_activate(ev)) { + if(_lwevent_activate(EPOLL_CTL_ADD, ev)) { free(ev); return 0; } @@ -79,7 +80,7 @@ int lwevent_deactivate(struct lwevent* ev) */ int lwevent_reactivate(struct lwevent* ev) { - return _lwevent_activate(ev); + return _lwevent_activate(EPOLL_CTL_ADD, ev); } diff --git a/src/liblwevent/300_fdevent.h b/src/liblwevent/300_fdevent.h index a7ab780..e43cb64 100644 --- a/src/liblwevent/300_fdevent.h +++ b/src/liblwevent/300_fdevent.h @@ -28,14 +28,13 @@ retrieve associated values. */ struct lwevent -#ifndef DOXYGEN -; -#else +#ifdef DOXYGEN { /*! \brief Dummy variable for doxygen */ int dummy; -}; +} #endif +; diff --git a/src/liblwevent/350_fdparams.c b/src/liblwevent/350_fdparams.c new file mode 100644 index 0000000..d23b24f --- /dev/null +++ b/src/liblwevent/350_fdparams.c @@ -0,0 +1,93 @@ +/* liblwevent/src/liblwevent/300_fdparams.c + * + * (c)2007, Laurence Withers, . + * Released under the GNU GPLv2. See file COPYING or + * http://www.gnu.org/copyleft/gpl.html for details. +*/ + + + +/* lwevent_get_*() + * Functions for retrieving attributes stored in a struct lwevent. + */ +int lwevent_get_fd(const struct lwevent* ev) +{ + return ev->fd; +} + +int lwevent_get_events(const struct lwevent* ev) +{ + return ev->events; +} + +lwevent_callback lwevent_get_callback(const struct lwevent* ev) +{ + return ev->callback; +} + +void* lwevent_get_user(const struct lwevent* ev) +{ + return ev->user; +} + +lwevent_dtor lwevent_get_dtor(const struct lwevent* ev) +{ + return ev->dtor; +} + +int lwevent_get_autodestroy(const struct lwevent* ev) +{ + return ev->autodestroy; +} + + + +/* lwevent_set_events() + * Calls epoll_ctl(EPOLL_CTL_MOD) to change the events associated with the object `ev'. Does not + * perform the call if the new events match the current events. If `old_events' is not null, stores + * the existing events before changing. Returns -1 if epoll_ctl fails. + */ +int lwevent_set_events(struct lwevent* ev, int events, int* old_events) +{ + if(old_events) *old_events = ev->events; + if(events == ev->events) return 0; /* don't call epoll_ctl() if no change */ + + return _lwevent_activate(EPOLL_CTL_MOD, ev); +} + + + +/* lwevent_set_*() + * Changes attributes associated with `ev', possibly storing the old value. + */ +void lwevent_set_callback(struct lwevent* ev, lwevent_callback callback, + lwevent_callback* old_callback) +{ + if(old_callback) *old_callback = ev->callback; + ev->callback = callback; +} + +void lwevent_set_user(struct lwevent* ev, void* user, void** old_user) +{ + if(old_user) *old_user = ev->user; + ev->user = user; +} + +void lwevent_set_dtor(struct lwevent* ev, lwevent_dtor dtor, lwevent_dtor* old_dtor) +{ + if(old_dtor) *old_dtor = ev->dtor; + ev->dtor = dtor; +} + +void lwevent_set_autodestroy(struct lwevent* ev, int autodestroy, int* old_autodestroy) +{ + if(old_autodestroy) *old_autodestroy = ev->autodestroy; + ev->autodestroy = autodestroy; +} + + + +/* options for text editors +kate: replace-trailing-space-save true; space-indent true; tab-width 4; +vim: expandtab:ts=4:sw=4 +*/ diff --git a/src/liblwevent/350_fdparams.h b/src/liblwevent/350_fdparams.h index b97b3c4..afb3a02 100644 --- a/src/liblwevent/350_fdparams.h +++ b/src/liblwevent/350_fdparams.h @@ -1,4 +1,4 @@ -/* liblwevent/src/liblwevent/300_fdevent.h +/* liblwevent/src/liblwevent/300_fdparams.h * * (c)2007, Laurence Withers, . * Released under the GNU GPLv2. See file COPYING or @@ -29,15 +29,6 @@ typedef void (*lwevent_dtor)(struct lwevent* ev); -/*! \brief Flag: autodestroy object if \c EPOLLERR is received. */ -extern const int LWEVENT_AUTODESTROY_ERR; -/*! \brief Flag: autodestroy object if \c EPOLLRDHUP is received. */ -extern const int LWEVENT_AUTODESTROY_RDHUP; -/*! \brief Flag: autodestroy object if \c EPOLLHUP is received. */ -extern const int LWEVENT_AUTODESTROY_WRHUP; - - - /*! \brief Retrieve file descriptor associated with object. */ int lwevent_get_fd(const struct lwevent* ev) __attribute__((nonnull, warn_unused_result)); @@ -145,7 +136,9 @@ void lwevent_set_dtor(struct lwevent* ev, lwevent_dtor dtor, lwevent_dtor* old_d \param[out] old_autodestroy Previous bitmask stored here. May be 0. The autodestroy functionality causes the event object \a ev to be automatically passed to -lwevent_free() if any specified event occurs. You may pass ~0 to autodestroy on any error event. +lwevent_free() if any specified event occurs. You may pass ~0 to autodestroy on any event. See +\c epoll_ctl(2) for a list of valid event flags (non-authoritative list: \c EPOLLIN, \c EPOLLOUT, +\c EPOLLRDHUP, \c EPOLLPRI, \c EPOLLERR, \c EPOLLHUP). */ void lwevent_set_autodestroy(struct lwevent* ev, int autodestroy, int* old_autodestroy) diff --git a/src/liblwevent/400_signalfd.c b/src/liblwevent/400_signalfd.c new file mode 100644 index 0000000..a48a3b6 --- /dev/null +++ b/src/liblwevent/400_signalfd.c @@ -0,0 +1,145 @@ +/* liblwevent/src/liblwevent/400_signalfd.c + * + * (c)2007, Laurence Withers, . + * Released under the GNU GPLv2. See file COPYING or + * http://www.gnu.org/copyleft/gpl.html for details. +*/ + + + +int signalfd(int fd, const sigset_t* mask) +{ + size_t sz; + sz = sizeof(sigset_t); + return syscall(__NR_signalfd, fd, mask, sz); +} + + + +struct lwevent_signal { + sigset_t mask; + lwevent_signalcb callback; + struct lwevent* ev; +}; + + + +static void _lwevent_signalfd_callback(struct lwevent* ev, int revents) +{ + (void)revents; + struct signalfd_siginfo ssi; + struct lwevent_signal* si; + + si = lwevent_get_user(ev); + + while(1) { + if(TEMP_FAILURE_RETRY( read(lwevent_get_fd(ev), &ssi, sizeof(ssi)) ) == -1) { + if(errno == EAGAIN) return; + /* TODO: now what??? */ + } + + si->callback(&ssi); + } +} + +struct lwevent_signal* lwevent_signalfd(const sigset_t* mask, lwevent_signalcb callback) +{ + struct lwevent_signal* si; + int fd; + + si = malloc(sizeof(struct lwevent_signal)); + if(!si) return 0; + memset(si, 0, sizeof(struct lwevent_signal)); + + si->mask = *mask; + sigdelset(&si->mask, SIGBUS); + sigdelset(&si->mask, SIGFPE); + sigdelset(&si->mask, SIGILL); + sigdelset(&si->mask, SIGSEGV); + si->callback = callback; + + fd = signalfd(-1, &si->mask); + if(fd == -1) { + free(si); + return 0; + } + + /* TODO: make non-blocking */ + + si->ev = lwevent_new(fd, EPOLLIN, _lwevent_signalfd_callback); + if(!si->ev) { + close(fd); + free(si); + return 0; + } + + lwevent_set_user(si->ev, si, 0); + return si; +} + + + +void lwevent_signalfd_getsigs(const struct lwevent_signal* si, sigset_t* mask) +{ + *mask = si->mask; +} + + + +lwevent_signalcb lwevent_signalfd_getcb(const struct lwevent_signal* si) +{ + return si->callback; +} + + + +int lwevent_signalfd_setsigs(struct lwevent_signal* si, const sigset_t* mask, sigset_t* old_mask) +{ + sigset_t mnew; + + if(old_mask) *old_mask = si->mask; + + mnew = *mask; + sigdelset(&mnew, SIGBUS); + sigdelset(&mnew, SIGFPE); + sigdelset(&mnew, SIGILL); + sigdelset(&mnew, SIGSEGV); + + if(signalfd(lwevent_get_fd(si->ev), &mnew)) return -1; + si->mask = mnew; + return 0; +} + + + +void lwevent_signalfd_setcb(struct lwevent_signal* si, lwevent_signalcb callback, + lwevent_signalcb* old_callback) +{ + if(old_callback) *old_callback = si->callback; + si->callback = callback; +} + + + +void lwevent_signalfd_close(struct lwevent_signal* si) +{ + if(!si) return; + + lwevent_free(si->ev); + free(si); +} + + + +struct lwevent_signal* lwevent_signalfd_default(void) +{ + /* TODO */ + return lwevent_signalfd(...); +} + + + +/* options for text editors +kate: replace-trailing-space-save true; space-indent true; tab-width 4; +vim: expandtab:ts=4:sw=4 +*/ diff --git a/src/liblwevent/400_signalfd.h b/src/liblwevent/400_signalfd.h new file mode 100644 index 0000000..8b5a6a1 --- /dev/null +++ b/src/liblwevent/400_signalfd.h @@ -0,0 +1,188 @@ +/* liblwevent/src/liblwevent/400_signalfd.h + * + * (c)2007, Laurence Withers, . + * Released under the GNU GPLv2. See file COPYING or + * http://www.gnu.org/copyleft/gpl.html for details. +*/ + + + +/*! \defgroup signalfd Signals through file descriptor + +This module provides an interface to \c signalfd(2), a system call which provides an alternative +signal delivery mechanism: certain signals can be read from a file descriptor queue. + +\todo Currently, the \c signalfd(2) interface is very new (in fact, the manpage has not yet been + released and must be pieced together from http://lkml.org/lkml/2007/9/27/88). Furthermore, it + is not yet implemented in \a glibc. Until that occurs, a function call wrapper for the system + call is provided in this library. Eventually this will be removed in favour of the authoritative + \a glibc interface. + +*/ +/*!@{*/ + + + +/*! \brief Signal info structure. + +This is the structure that is returned from a \c read(2) of a signalfd. + +\todo Eventually, this will be folded into glibc. At that time, this structure definition will have + to be removed and the code updated to use the new stuff. + +*/ +struct signalfd_siginfo { + uint32_t ssi_signo; /*!< si_signo */ + int32_t ssi_err; /*!< si_errno */ + int32_t ssi_code; /*!< si_code */ + uint32_t ssi_pid; /*!< si_pid */ + uint32_t ssi_uid; /*!< si_uid */ + int32_t ssi_fd; /*!< si_fd */ + uint32_t ssi_tid; /*!< si_tid */ + uint32_t ssi_band; /*!< si_band */ + uint32_t ssi_overrun; /*!< si_overrun */ + uint32_t ssi_trapno; /*!< si_trapno */ + int32_t ssi_status; /*!< si_status */ + int32_t ssi_svint; /*!< si_int */ + uint64_t ssi_svptr; /*!< si_ptr */ + uint64_t ssi_utime; /*!< si_utime */ + uint64_t ssi_stime; /*!< si_stime */ + uint64_t ssi_addr; /*!< si_addr */ + uint8_t ssi_pad[48]; /*!< Pad size to 128 bytes (allow space for additional fields in the future) */ +}; + + + +/*! \brief System call wrapper for \c signalfd(2). + +\param fd File descriptor to modify (pass -1 to create a new file descriptor). +\param mask Signal mask. Any signals set in \a mask will be delivered through \a fd (but see below). +\retval 0 on success. +\retval -1 on error (and see \a errno). + +System call wrapper function for \c signalfd(2). Any signals turned on in \a mask will be delivered +through the file descriptor \a fd. However, these signals should also be masked from the process +using \c sigprocmask(2), since otherwise the two delivery mechanisms will race. + +Passing -1 in \a fd causes a new file descriptor to be allocated. It can be polled, closed and read +normally. It will return struct signalfd_siginfo objects when read. Passing an existing signalfd +file descriptor in \a fd will allow the \a mask to be modified. + +\todo Eventually, this system call wrapper function will be removed in favour of \a glibc's + authoritative version. + +*/ +int signalfd(int fd, const sigset_t* mask); + + + +/*! \brief Event-driven signal receiver object. + +This object implements an event-driven signal receiver based on \c signalfd(2). The object contains +details such as the callback function for each received signal. + +*/ +struct lwevent_signal +#ifdef DOXYGEN +{ + /*! \brief Dummy variable for doxygen; this structure is really opaque. */ + int dummy; +} +#endif +; + + + +/*! \brief Pointer to signal callback function. */ +typedef void (*lwevent_signalcb)(const struct signalfd_siginfo* ssi); + + + +/*! \brief Create and register event-driven signal receiver object. + +\param mask Set of signals to catch. +\param callback Callback for received signals. +\returns Pointer to new receiver object. +\retval 0 on error (and see \a errno). + +Creates a signal receiver file descriptor using \c signalfd(2), and requests that the signals in +\a mask are received through it. Also calls \c sigprocmask(2) to block the signals in \a mask, and +ensure the delivery mechanisms do not race. + +This function will silently ignore attempts to receive \c SIGKILL, \c SIGSTOP, \c SIGBUS, \c SIGFPE, +\c SIGILL and \c SIGSEGV even if they are set in \a mask. Such signals either cannot be redirected +or would cause serious misbehaviour if they were not received immediately through the normal +mechanism. + +*/ +struct lwevent_signal* lwevent_signalfd(const sigset_t* mask, lwevent_signalcb callback) + __attribute__((malloc,nonnull,warn_unused_result)); + + + +/*! \brief Retrieve signal receiver's signal set. */ +void lwevent_signalfd_getsigs(const struct lwevent_signal* si, sigset_t* mask) + __attribute__((nonnull)); + +/*! \brief Retrieve signal receiver's callback function. */ +lwevent_signalcb lwevent_signalfd_getcb(const struct lwevent_signal* si) + __attribute__((nonnull)); + + + +/*! \brief Modify signal receiver's signal set. + +\param si Signal receiver object. +\param mask The new mask to enact. +\param oldmask The old mask. May be 0. +\retval 0 on success. +\retval -1 on error (and see \a errno). + +Changes the set of signals being received by \a si to those set in \a mask. See lwevent_signalfd() +for a discussion of the full behaviour of the receiver. The application may retrieve the previous +mask by passing a pointer to a signal set in \a oldmask. + +*/ +int lwevent_signalfd_setsigs(struct lwevent_signal* si, const sigset_t* mask, sigset_t* oldmask) + __attribute__((nonnull,warn_unused_result)); + + + +/*! \brief Modiy signal receiver's callback function. */ +void lwevent_signalfd_setcb(struct lwevent_signal* si, lwevent_signalcb callback, + lwevent_signalcb* old_callback) __attribute__((nonnull)); + + + +/*! \brief Close signal receiver. + +\param si Signal receiver object. May be 0. + +Closes a signal receiver object, unblocking the signals it blocked with \c sigprocmask(2). If \a si +is null, this is a no-op. + +*/ +void lwevent_signalfd_close(struct lwevent_signal* si); + + + +/*! \brief Set up a default signal receiver. + +\returns Pointer to new receiver object. +\retval 0 on error (and see \a errno). + +Sets up a default signal receiver object. This object will catch \c SIGINT and \c SIGTERM and set +the lwevent_loop_exit flag to non-zero. Other signals are left for the normal delivery mechanism. + +*/ +struct lwevent_signal* lwevent_signalfd_default(void) __attribute__((malloc,warn_unused_result)); + + + +/*!@}*/ + + +/* options for text editors +kate: replace-trailing-space-save true; space-indent true; tab-width 4; +vim: expandtab:ts=4:sw=4:syntax=c.doxygen +*/