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