1/*
2 * Copyright (c) 2009 Mark Heily <mark@heily.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <errno.h>
18#include <fcntl.h>
19#include <pthread.h>
20#include <signal.h>
21#include <stdlib.h>
22#include <stdio.h>
23#include <sys/epoll.h>
24#include <sys/queue.h>
25#include <sys/socket.h>
26#include <sys/types.h>
27#include <string.h>
28#include <time.h>
29#include <unistd.h>
30
31/* Linux equivalents to kqueue(2) */
32#include <sys/timerfd.h>
33
34#include "sys/event.h"
35#include "private.h"
36
37#ifndef NDEBUG
38static char *
39itimerspec_dump(struct itimerspec *ts)
40{
41    static char __thread buf[1024];
42
43    snprintf(buf, sizeof(buf),
44            "itimer: [ interval=%lu s %lu ns, next expire=%lu s %lu ns ]",
45            ts->it_interval.tv_sec,
46            ts->it_interval.tv_nsec,
47            ts->it_value.tv_sec,
48            ts->it_value.tv_nsec
49           );
50
51    return (buf);
52}
53#endif
54
55/* Convert milliseconds into seconds+nanoseconds */
56static void
57convert_msec_to_itimerspec(struct itimerspec *dst, int src, int oneshot)
58{
59    time_t sec, nsec;
60
61    sec = src / 1000;
62    nsec = (src % 1000) * 1000000;
63
64    /* Set the interval */
65    if (oneshot) {
66        dst->it_interval.tv_sec = 0;
67        dst->it_interval.tv_nsec = 0;
68    } else {
69        dst->it_interval.tv_sec = sec;
70        dst->it_interval.tv_nsec = nsec;
71    }
72
73    /* Set the initial expiration */
74    dst->it_value.tv_sec = sec;
75    dst->it_value.tv_nsec = nsec;
76    dbg_printf("%s", itimerspec_dump(dst));
77}
78
79static int
80ktimer_delete(struct filter *filt, struct knote *kn)
81{
82    int rv = 0;
83
84    if (kn->data.pfd == -1)
85        return (0);
86
87    dbg_printf("removing timerfd %d from %d", kn->data.pfd, filt->kf_pfd);
88    if (epoll_ctl(filt->kf_pfd, EPOLL_CTL_DEL, kn->data.pfd, NULL) < 0) {
89        dbg_printf("epoll_ctl(2): %s", strerror(errno));
90        rv = -1;
91    }
92    if (close(kn->data.pfd) < 0) {
93        dbg_printf("close(2): %s", strerror(errno));
94        rv = -1;
95    }
96
97    kn->data.pfd = -1;
98    return (rv);
99}
100
101int
102evfilt_timer_init(struct filter *filt)
103{
104    filt->kf_pfd = epoll_create(1);
105    if (filt->kf_pfd < 0)
106        return (-1);
107
108    dbg_printf("timer epollfd = %d", filt->kf_pfd);
109    return (0);
110}
111
112void
113evfilt_timer_destroy(struct filter *filt)
114{
115    close (filt->kf_pfd);//LAME
116}
117
118/* TODO: This entire function is copy+pasted from socket.c
119   with minor changes for timerfds.
120   Perhaps it could be refactored into a generic epoll_copyout()
121   that calls custom per-filter actions.
122   */
123int
124evfilt_timer_copyout(struct filter *filt,
125            struct kevent *dst,
126            int nevents)
127{
128    struct epoll_event epevt[MAX_KEVENT];
129    struct epoll_event *ev;
130    struct knote *kn;
131    uint64_t expired;
132    int i, nret;
133    ssize_t n;
134
135    for (;;) {
136        nret = epoll_wait(filt->kf_pfd, &epevt[0], nevents, 0);
137        if (nret < 0) {
138            if (errno == EINTR)
139                continue;
140            dbg_perror("epoll_wait");
141            return (-1);
142        } else {
143            break;
144        }
145    }
146
147    for (i = 0, nevents = 0; i < nret; i++) {
148        ev = &epevt[i];
149        /* TODO: put in generic debug.c: epoll_event_dump(ev); */
150        kn = ev->data.ptr;
151        memcpy(dst, &kn->kev, sizeof(*dst));
152        if (ev->events & EPOLLERR)
153            dst->fflags = 1; /* FIXME: Return the actual timer error */
154
155        /* On return, data contains the number of times the
156           timer has been trigered.
157             */
158        n = read(kn->data.pfd, &expired, sizeof(expired));
159        if (n < 0 || n < sizeof(expired)) {
160            dbg_puts("invalid read from timerfd");
161            expired = 1;  /* Fail gracefully */
162        }
163        dst->data = expired;
164
165        if (kn->kev.flags & EV_DISPATCH) {
166            KNOTE_DISABLE(kn);
167            ktimer_delete(filt, kn);
168        } else if (kn->kev.flags & EV_ONESHOT) {
169            ktimer_delete(filt, kn);
170            knote_free(filt, kn);
171        }
172
173        nevents++;
174        dst++;
175    }
176
177    return (nevents);
178}
179
180int
181evfilt_timer_knote_create(struct filter *filt, struct knote *kn)
182{
183    struct epoll_event ev;
184    struct itimerspec ts;
185    int tfd;
186
187    kn->kev.flags |= EV_CLEAR;
188
189    tfd = timerfd_create(CLOCK_MONOTONIC, 0);
190    if (tfd < 0) {
191        dbg_printf("timerfd_create(2): %s", strerror(errno));
192        return (-1);
193    }
194    dbg_printf("created timerfd %d", tfd);
195
196    convert_msec_to_itimerspec(&ts, kn->kev.data, kn->kev.flags & EV_ONESHOT);
197    if (timerfd_settime(tfd, 0, &ts, NULL) < 0) {
198        dbg_printf("timerfd_settime(2): %s", strerror(errno));
199        close(tfd);
200        return (-1);
201    }
202
203    memset(&ev, 0, sizeof(ev));
204    ev.events = EPOLLIN;
205    ev.data.ptr = kn;
206    if (epoll_ctl(filt->kf_pfd, EPOLL_CTL_ADD, tfd, &ev) < 0) {
207        dbg_printf("epoll_ctl(2): %d", errno);
208        close(tfd);
209        return (-1);
210    }
211
212    kn->data.pfd = tfd;
213    return (0);
214}
215
216int
217evfilt_timer_knote_modify(struct filter *filt, struct knote *kn,
218        const struct kevent *kev)
219{
220    return (0); /* STUB */
221}
222
223int
224evfilt_timer_knote_delete(struct filter *filt, struct knote *kn)
225{
226    return (ktimer_delete(filt,kn));
227}
228
229int
230evfilt_timer_knote_enable(struct filter *filt, struct knote *kn)
231{
232    return evfilt_timer_knote_create(filt, kn);
233}
234
235int
236evfilt_timer_knote_disable(struct filter *filt, struct knote *kn)
237{
238    return evfilt_timer_knote_delete(filt, kn);
239}
240
241const struct filter evfilt_timer = {
242    EVFILT_TIMER,
243    evfilt_timer_init,
244    evfilt_timer_destroy,
245    evfilt_timer_copyout,
246    evfilt_timer_knote_create,
247    evfilt_timer_knote_modify,
248    evfilt_timer_knote_delete,
249    evfilt_timer_knote_enable,
250    evfilt_timer_knote_disable,
251};
252