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/queue.h>
24#include <sys/socket.h>
25#include <sys/types.h>
26#include <string.h>
27#include <time.h>
28#include <unistd.h>
29
30#include "sys/event.h"
31#include "private.h"
32
33#ifndef NDEBUG
34static char *
35itimerspec_dump(struct itimerspec *ts)
36{
37    static char __thread buf[1024];
38
39    snprintf(buf, sizeof(buf),
40            "itimer: [ interval=%lu s %lu ns, next expire=%lu s %lu ns ]",
41            ts->it_interval.tv_sec,
42            ts->it_interval.tv_nsec,
43            ts->it_value.tv_sec,
44            ts->it_value.tv_nsec
45           );
46
47    return (buf);
48}
49#endif
50
51/* Convert milliseconds into seconds+nanoseconds */
52static void
53convert_msec_to_itimerspec(struct itimerspec *dst, int src, int oneshot)
54{
55    time_t sec, nsec;
56
57    sec = src / 1000;
58    nsec = (src % 1000) * 1000000;
59
60    /* Set the interval */
61    if (oneshot) {
62        dst->it_interval.tv_sec = 0;
63        dst->it_interval.tv_nsec = 0;
64    } else {
65        dst->it_interval.tv_sec = sec;
66        dst->it_interval.tv_nsec = nsec;
67    }
68
69    /* Set the initial expiration */
70    dst->it_value.tv_sec = sec;
71    dst->it_value.tv_nsec = nsec;
72    dbg_printf("%s", itimerspec_dump(dst));
73}
74
75static int
76ktimer_create(struct filter *filt, struct knote *kn)
77{
78    port_notify_t pn;
79    struct sigevent se;
80    struct itimerspec ts;
81    timer_t timerid;
82
83    kn->kev.flags |= EV_CLEAR;
84
85    pn.portnfy_port = filt->kf_kqueue->kq_port;
86    pn.portnfy_user = (void *) kn->kev.ident;
87
88    se.sigev_notify = SIGEV_PORT;
89    se.sigev_value.sival_ptr = &pn;
90
91    if (timer_create (CLOCK_MONOTONIC, &se, &timerid) < 0) {
92        dbg_perror("timer_create(2)");
93        return (-1);
94    }
95
96    convert_msec_to_itimerspec(&ts, kn->kev.data, kn->kev.flags & EV_ONESHOT);
97    if (timer_settime(timerid, 0, &ts, NULL) < 0) {
98        dbg_perror("timer_settime(2)");
99        (void) timer_delete(timerid);
100        return (-1);
101    }
102
103    kn->data.timerid = timerid;
104    dbg_printf("created timer with id #%lu", (unsigned long) timerid);
105
106    return (0);
107}
108
109int
110evfilt_timer_init(struct filter *filt)
111{
112    return (0);
113}
114
115void
116evfilt_timer_destroy(struct filter *filt)
117{
118    return;
119}
120
121int
122evfilt_timer_copyout(struct filter *filt,
123            struct kevent *dst,
124            int nevents)
125{
126    port_event_t *pe = (port_event_t *) pthread_getspecific(filt->kf_kqueue->kq_port_event);
127    long buf;
128    timer_t timerid;
129    struct knote *kn;
130
131    /* XXX-FIXME: danger here -- there has to be a better way */
132    buf = (long) pe->portev_user;
133    timerid = (timer_t) buf;
134    /* ^^^^^^^^^ */
135    kn = knote_lookup(filt, timerid);
136
137    dbg_printf("knote=%p", kn);
138    memcpy(dst, &kn->kev, sizeof(*dst));
139    //TODO:
140    //if (ev->events & EPOLLERR)
141    //    dst->fflags = 1; /* FIXME: Return the actual timer error */
142
143    /* FIXME: On return, data contains the number of times the
144       timer has been trigered.
145     */
146    dst->data = 1;  //workaround
147
148    if (kn->kev.flags & EV_DISPATCH) {
149        KNOTE_DISABLE(kn);
150        timer_delete(kn->data.timerid);
151    } else if (kn->kev.flags & EV_ONESHOT) {
152        timer_delete(kn->data.timerid);
153        knote_free(filt, kn);
154    }
155
156    return (1);
157}
158
159int
160evfilt_timer_knote_create(struct filter *filt, struct knote *kn)
161{
162    return ktimer_create(filt, kn);
163}
164
165int
166evfilt_timer_knote_modify(struct filter *filt, struct knote *kn,
167        const struct kevent *kev)
168{
169    return (-1); /* STUB */
170}
171
172int
173evfilt_timer_knote_delete(struct filter *filt, struct knote *kn)
174{
175    if (kn->kev.flags & EV_DISABLE)
176        return (0);
177
178    dbg_printf("deleting timer # %d", kn->data.timerid);
179    return timer_delete(kn->data.timerid);
180}
181
182int
183evfilt_timer_knote_enable(struct filter *filt, struct knote *kn)
184{
185    return evfilt_timer_knote_create(filt, kn);
186}
187
188int
189evfilt_timer_knote_disable(struct filter *filt, struct knote *kn)
190{
191    return evfilt_timer_knote_delete(filt, kn);
192}
193
194const struct filter evfilt_timer = {
195    EVFILT_TIMER,
196    evfilt_timer_init,
197    evfilt_timer_destroy,
198    evfilt_timer_copyout,
199    evfilt_timer_knote_create,
200    evfilt_timer_knote_modify,
201    evfilt_timer_knote_delete,
202    evfilt_timer_knote_enable,
203    evfilt_timer_knote_disable,
204};
205