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