#include #include "timer-socket.h" #if defined(AF_TIMER) && !defined(SUPPRESS_AF_TIMER) void timer_socket_init(void) { } int timer_socket(void) { return(socket(AF_TIMER,SOCK_STREAM,0)); } #else #include #include #include #include #include #include #include #include #include #include #ifdef OLD_CMSG_INTERFACE #define CMSPACE(x) (sizeof(struct cmsghdr)+(x)) #define CMLEN(x) (sizeof(struct cmsghdr)+(x)) #define CMSKIP(x) (sizeof(struct cmsghdr)) #else #define CMSPACE(x) (CMSG_SPACE((x))) #define CMLEN(x) (CMSG_LEN((x))) #define CMSKIP(x) ((char *)CMSG_DATA((x))-(char *)(x)) #endif extern const char *__progname; static int tfsock; void timer_socket_init(void) { int p[2]; if (socketpair(AF_LOCAL,SOCK_STREAM,0,&p[0]) < 0) { fprintf(stderr,"%s: socketpair: %s\n",__progname,strerror(errno)); exit(1); } if (fork() == 0) { close(p[0]); tfsock = p[1]; setproctitle("(timer socket manager)"); while (1) { struct msghdr mh; struct cmsghdr cmh; struct iovec iov; char junk; unsigned char ctlbuf[CMSPACE(sizeof(int))]; int fd; int rv; sigset_t m; sigfillset(&m); sigprocmask(SIG_SETMASK,&m,0); while (wait4(-1,0,WNOHANG,0) > 0) ; iov.iov_base = &junk; iov.iov_len = 1; mh.msg_name = 0; mh.msg_namelen = 0; mh.msg_iov = &iov; mh.msg_iovlen = 1; mh.msg_control = &ctlbuf[0]; mh.msg_controllen = CMSPACE(sizeof(int)); mh.msg_flags = 0; rv = recvmsg(tfsock,&mh,0); if (rv < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: continue; break; } fprintf(stderr,"%s: recvmsg: %s\n",__progname,strerror(errno)); exit(1); } if (rv == 0) exit(0); if (mh.msg_controllen < sizeof(struct cmsghdr)) { fprintf(stderr,"%s: control length (%d) too small\n",__progname,(int)mh.msg_controllen); exit(1); } bcopy(&ctlbuf[0],&cmh,sizeof(struct cmsghdr)); if ((cmh.cmsg_level != SOL_SOCKET) || (cmh.cmsg_type != SCM_RIGHTS)) { fprintf(stderr,"%s: level/type wrong\n",__progname); exit(1); } if (cmh.cmsg_len < CMLEN(sizeof(int))) { fprintf(stderr,"%s: cmsg_len (%d) too small\n",__progname,(int)cmh.cmsg_len); exit(1); } bcopy(&ctlbuf[CMSKIP(&cmh)],&fd,sizeof(int)); if (fork() == 0) { struct sigaction sa; int p[2]; struct pollfd pfd[2]; int rv; static void sigalrm_handler(int sig __attribute__((__unused__))) { write(p[1],"",1); } setproctitle("(timer socket process)"); sigdelset(&m,SIGALRM); sigprocmask(SIG_SETMASK,&m,0); close(tfsock); tfsock = fd; sa.sa_handler = &sigalrm_handler; sigfillset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGALRM,&sa,0); pipe(&p[0]); while (1) { pfd[0] = (struct pollfd) { .fd = tfsock, .events = POLLIN|POLLRDNORM }; pfd[1] = (struct pollfd) { .fd = p[0], .events = POLLIN|POLLRDNORM }; rv = poll(&pfd[0],2,INFTIM); if (rv < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: continue; break; } fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno)); exit(1); } if (pfd[0].revents & (POLLIN|POLLRDNORM)) { struct itimerval itv; rv = recv(tfsock,&itv,sizeof(itv),MSG_WAITALL); if (rv < sizeof(itv)) exit(0); setitimer(ITIMER_REAL,&itv,0); } if (pfd[1].revents & (POLLIN|POLLRDNORM)) { struct timersock_event e; read(p[0],&e,1); write(tfsock,&e,sizeof(e)); } } } close(fd); } _exit(0); } close(p[1]); tfsock = p[0]; return; } int timer_socket(void) { int e; int p[2]; struct msghdr mh; struct cmsghdr cmh; struct iovec iov; char junk; unsigned char ctlbuf[CMSPACE(sizeof(int))]; if (socketpair(AF_LOCAL,SOCK_STREAM,0,&p[0]) < 0) return(-1); iov.iov_base = &junk; iov.iov_len = 1; cmh.cmsg_len = CMLEN(sizeof(int)); cmh.cmsg_level = SOL_SOCKET; cmh.cmsg_type = SCM_RIGHTS; bcopy(&cmh,&ctlbuf[0],sizeof(struct cmsghdr)); bcopy(&p[0],&ctlbuf[CMSKIP(&cmh)],sizeof(int)); mh.msg_name = 0; mh.msg_namelen = 0; mh.msg_iov = &iov; mh.msg_iovlen = 1; mh.msg_control = (void *) &ctlbuf[0]; mh.msg_controllen = CMLEN(sizeof(int)); mh.msg_flags = 0; if (sendmsg(tfsock,&mh,0) < 0) { e = errno; close(p[0]); close(p[1]); errno = e; return(-1); } close(p[0]); return(p[1]); } #endif #ifdef TESTBED #include #include #include #include static int tvcmp(struct timeval t1, struct timeval t2) { if (t1.tv_sec < t2.tv_sec) return(-1); if (t1.tv_sec > t2.tv_sec) return(1); if (t1.tv_usec < t2.tv_usec) return(-1); if (t1.tv_usec > t2.tv_usec) return(1); return(0); } static struct timeval tvsub(struct timeval t1, struct timeval t2) { if (t2.tv_usec > t1.tv_usec) { return((struct timeval) { .tv_sec = t1.tv_sec - t2.tv_sec - 1, .tv_usec = t1.tv_usec + 1000000 - t2.tv_usec }); } else { return((struct timeval) { .tv_sec = t1.tv_sec - t2.tv_sec, .tv_usec = t1.tv_usec - t2.tv_usec }); } } static struct timeval tvadd(struct timeval t1, struct timeval t2) { if (t2.tv_usec+t1.tv_usec >= 1000000) { return((struct timeval) { .tv_sec = t1.tv_sec + t2.tv_sec + 1, .tv_usec = (t1.tv_usec + t2.tv_usec) - 1000000 }); } else { return((struct timeval) { .tv_sec = t1.tv_sec + t2.tv_sec, .tv_usec = t1.tv_usec + t2.tv_usec }); } } static void test1(void) { int s; struct itimerval itv; struct timeval t1; struct timeval t2; struct timeval td; struct timersock_event ev; s = timer_socket(); itv.it_value.tv_sec = 3; itv.it_value.tv_usec = 0; itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; gettimeofday(&t1,0); write(s,&itv,sizeof(itv)); read(s,&ev,sizeof(ev)); gettimeofday(&t2,0); close(s); td = tvsub(t2,t1); printf("setting = 3, actual = %lu.%06lu\n", (unsigned long int)td.tv_sec, (unsigned long int)td.tv_usec); } static void test2(void) { int s; struct itimerval itv; struct timeval t1; struct timeval t2; struct timeval td; struct timeval des; struct timeval err; struct timersock_event ev; int n; s = timer_socket(); itv.it_value.tv_sec = 3; itv.it_value.tv_usec = 141593; itv.it_interval.tv_sec = 1; itv.it_interval.tv_usec = 123456; gettimeofday(&t1,0); write(s,&itv,sizeof(itv)); n = 0; des = itv.it_value; while (1) { read(s,&ev,sizeof(ev)); gettimeofday(&t2,0); td = tvsub(t2,t1); printf("setting = %lu.%06lu, actual = %lu.%06lu, error = ", (unsigned long int)des.tv_sec, (unsigned long int)des.tv_usec, (unsigned long int)td.tv_sec, (unsigned long int)td.tv_usec); if (tvcmp(des,td) < 0) { err = tvsub(td,des); printf("-%lu.%06lu\n", (unsigned long int)err.tv_sec, (unsigned long int)err.tv_usec); } else { err = tvsub(des,td); printf("%lu.%06lu\n", (unsigned long int)err.tv_sec, (unsigned long int)err.tv_usec); } des = tvadd(des,itv.it_interval); n ++; if (n > 5) break; } close(s); } static void test3(void) { int s[5]; struct itimerval itv; struct timeval t1; struct timeval t2; struct timeval td; struct timersock_event ev; int i; gettimeofday(&t1,0); for (i=5-1;i>=0;i--) { s[i] = timer_socket(); itv.it_value.tv_sec = 1 + i; itv.it_value.tv_usec = 0; itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; write(s[i],&itv,sizeof(itv)); } for (i=0;i<5;i++) { read(s[i],&ev,sizeof(ev)); gettimeofday(&t2,0); td = tvsub(t2,t1); printf("setting = %d, actual = %lu.%06lu\n", 1 + i, (unsigned long int)td.tv_sec, (unsigned long int)td.tv_usec); } for (i=0;i<5;i++) close(s[i]); } int main(void); int main(void) { timer_socket_init(); test1(); test2(); test3(); return(0); } #endif