1/*
2 * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * timer.c
26 * - timer functions
27 */
28
29/*
30 * Modification History
31 *
32 * May 8, 2000	Dieter Siegmund (dieter@apple)
33 * - created
34 */
35
36#include <stdlib.h>
37#include <unistd.h>
38#include <string.h>
39#include <stdio.h>
40#include <sys/types.h>
41#include <sys/time.h>
42#include <math.h>
43#include <syslog.h>
44#include <notify.h>
45#include <notify_keys.h>
46#ifndef kNotifyClockSet
47#define kNotifyClockSet "com.apple.system.clock_set"
48#endif
49#include "globals.h"
50#include "util.h"
51#include "timer.h"
52
53#include <CoreFoundation/CFRunLoop.h>
54#include <CoreFoundation/CFDate.h>
55
56struct timer_callout {
57    timer_func_t *	func;
58    void *		arg1;
59    void *		arg2;
60    void *		arg3;
61    CFRunLoopTimerRef	timer_source;
62    boolean_t		enabled;
63    uint32_t		time_generation;
64};
65
66#ifdef TEST_ARP_SESSION
67#define my_log	syslog
68#endif /* TEST_ARP_SESSION */
69
70/*
71 * Use notify(3) APIs to detect date/time changes
72 */
73
74static boolean_t	S_time_change_registered;
75static int		S_time_change_token;
76static uint32_t		S_time_generation;
77
78static void
79timer_notify_check(void)
80{
81    int		check = 0;
82    int		status;
83
84    if (S_time_change_registered == FALSE) {
85	return;
86    }
87    status = notify_check(S_time_change_token, &check);
88    if (status != NOTIFY_STATUS_OK) {
89	my_log(LOG_ERR, "timer: notify_check failed with %d", status);
90    }
91    else if (check != 0) {
92	S_time_generation++;
93    }
94    return;
95}
96
97static void
98timer_register_time_change(void)
99{
100    int		status;
101
102    if (S_time_change_registered) {
103	return;
104    }
105    status = notify_register_check(kNotifyClockSet, &S_time_change_token);
106    if (status != NOTIFY_STATUS_OK) {
107	my_log(LOG_ERR, "timer: notify_register_check(%s) failed, %d",
108	       kNotifyClockSet, status);
109	return;
110    }
111    S_time_change_registered = TRUE;
112
113    /* throw away the first check, since it always says check=1 */
114    timer_notify_check();
115    return;
116}
117
118/**
119 ** Timer callout functions
120 **/
121static void
122timer_callout_process(CFRunLoopTimerRef timer_source, void * info)
123{
124    timer_callout_t *	callout = (timer_callout_t *)info;
125
126    if (callout->func && callout->enabled) {
127	callout->enabled = FALSE;
128	(*callout->func)(callout->arg1, callout->arg2, callout->arg3);
129    }
130    return;
131}
132
133timer_callout_t *
134timer_callout_init()
135{
136    timer_callout_t * callout;
137
138    callout = malloc(sizeof(*callout));
139    if (callout == NULL)
140	return (NULL);
141    bzero(callout, sizeof(*callout));
142    timer_register_time_change();
143    callout->time_generation = S_time_generation;
144    return (callout);
145}
146
147void
148timer_callout_free(timer_callout_t * * callout_p)
149{
150    timer_callout_t * callout = *callout_p;
151
152    if (callout == NULL)
153	return;
154
155    timer_cancel(callout);
156    free(callout);
157    *callout_p = NULL;
158    return;
159}
160
161int
162timer_callout_set(timer_callout_t * callout,
163		  CFAbsoluteTime relative_time, timer_func_t * func,
164		  void * arg1, void * arg2, void * arg3)
165{
166    CFRunLoopTimerContext 	context =  { 0, NULL, NULL, NULL, NULL };
167    CFAbsoluteTime 		wakeup_time;
168
169    if (callout == NULL) {
170	return (0);
171    }
172    timer_cancel(callout);
173    if (func == NULL) {
174	return (0);
175    }
176    callout->func = func;
177    callout->arg1 = arg1;
178    callout->arg2 = arg2;
179    callout->arg3 = arg3;
180    callout->enabled = TRUE;
181    callout->time_generation = S_time_generation;
182    context.info = callout;
183    wakeup_time = CFAbsoluteTimeGetCurrent() + relative_time;
184    callout->timer_source
185	= CFRunLoopTimerCreate(NULL, wakeup_time,
186			       0.0, 0, 0,
187			       timer_callout_process,
188			       &context);
189    my_log(LOG_DEBUG, "timer: wakeup time is (%0.09g) %0.09g",
190	   relative_time, wakeup_time);
191    my_log(LOG_DEBUG, "timer: adding timer source");
192    CFRunLoopAddTimer(CFRunLoopGetCurrent(), callout->timer_source,
193		      kCFRunLoopDefaultMode);
194    return (1);
195}
196
197int
198timer_set_relative(timer_callout_t * callout,
199		   struct timeval rel_time, timer_func_t * func,
200		   void * arg1, void * arg2, void * arg3)
201{
202    CFTimeInterval	relative_time;
203
204    if (rel_time.tv_sec < 0) {
205	rel_time.tv_sec = 0;
206	rel_time.tv_usec = 1;
207    }
208    relative_time = rel_time.tv_sec
209	+ ((double)rel_time.tv_usec / USECS_PER_SEC);
210    return (timer_callout_set(callout, relative_time, func, arg1, arg2, arg3));
211}
212
213void
214timer_cancel(timer_callout_t * callout)
215{
216    if (callout == NULL) {
217	return;
218    }
219    callout->enabled = FALSE;
220    callout->func = NULL;
221    callout->time_generation = S_time_generation;
222    if (callout->timer_source) {
223	my_log(LOG_DEBUG, "timer:  freeing timer source");
224	CFRunLoopTimerInvalidate(callout->timer_source);
225	CFRelease(callout->timer_source);
226	callout->timer_source = 0;
227    }
228    return;
229}
230
231/**
232 ** timer functions
233 **/
234long
235timer_current_secs()
236{
237    return ((long)CFAbsoluteTimeGetCurrent());
238}
239
240struct timeval
241timer_current_time()
242{
243    double 		t = CFAbsoluteTimeGetCurrent();
244    struct timeval 	tv;
245
246    tv.tv_sec = (int32_t)t;
247    tv.tv_usec = (t - tv.tv_sec) * USECS_PER_SEC;
248
249    return (tv);
250}
251
252boolean_t
253timer_time_changed(timer_callout_t * entry)
254{
255    timer_notify_check();
256    return (entry->time_generation != S_time_generation);
257}
258
259boolean_t
260timer_still_pending(timer_callout_t * entry)
261{
262    return (entry->enabled);
263}
264