]> git.ipfire.org Git - thirdparty/chrony.git/blob - sched.c
examples: harden systemd services
[thirdparty/chrony.git] / sched.c
1 /*
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
5 * Copyright (C) Richard P. Curnow 1997-2003
6 * Copyright (C) Miroslav Lichvar 2011, 2013-2016
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of version 2 of the GNU General Public License as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 **********************************************************************
22
23 =======================================================================
24
25 This file contains the scheduling loop and the timeout queue.
26
27 */
28
29 #include "config.h"
30
31 #include "sysincl.h"
32
33 #include "array.h"
34 #include "sched.h"
35 #include "memory.h"
36 #include "util.h"
37 #include "local.h"
38 #include "logging.h"
39
40 /* ================================================== */
41
42 /* Flag indicating that we are initialised */
43 static int initialised = 0;
44
45 /* ================================================== */
46
47 /* One more than the highest file descriptor that is registered */
48 static unsigned int one_highest_fd;
49
50 #ifndef FD_SETSIZE
51 /* If FD_SETSIZE is not defined, assume that fd_set is implemented
52 as a fixed size array of bits, possibly embedded inside a record */
53 #define FD_SETSIZE (sizeof(fd_set) * 8)
54 #endif
55
56 typedef struct {
57 SCH_FileHandler handler;
58 SCH_ArbitraryArgument arg;
59 int events;
60 } FileHandlerEntry;
61
62 static ARR_Instance file_handlers;
63
64 /* Timestamp when last select() returned */
65 static struct timespec last_select_ts, last_select_ts_raw;
66 static double last_select_ts_err;
67
68 #define TS_MONO_PRECISION_NS 10000000U
69
70 /* Monotonic low-precision timestamp measuring interval since the start */
71 static double last_select_ts_mono;
72 static uint32_t last_select_ts_mono_ns;
73
74 /* ================================================== */
75
76 /* Variables to handler the timer queue */
77
78 typedef struct _TimerQueueEntry
79 {
80 struct _TimerQueueEntry *next; /* Forward and back links in the list */
81 struct _TimerQueueEntry *prev;
82 struct timespec ts; /* Local system time at which the
83 timeout is to expire. Clearly this
84 must be in terms of what the
85 operating system thinks of as
86 system time, because it will be an
87 argument to select(). Therefore,
88 any fudges etc that our local time
89 driver module would apply to time
90 that we pass to clients etc doesn't
91 apply to this. */
92 SCH_TimeoutID id; /* ID to allow client to delete
93 timeout */
94 SCH_TimeoutClass class; /* The class that the epoch is in */
95 SCH_TimeoutHandler handler; /* The handler routine to use */
96 SCH_ArbitraryArgument arg; /* The argument to pass to the handler */
97
98 } TimerQueueEntry;
99
100 /* The timer queue. We only use the next and prev entries of this
101 record, these chain to the real entries. */
102 static TimerQueueEntry timer_queue;
103 static unsigned long n_timer_queue_entries;
104 static SCH_TimeoutID next_tqe_id;
105
106 /* Pointer to head of free list */
107 static TimerQueueEntry *tqe_free_list = NULL;
108
109 /* Timestamp when was last timeout dispatched for each class */
110 static struct timespec last_class_dispatch[SCH_NumberOfClasses];
111
112 /* ================================================== */
113
114 /* Flag terminating the main loop, which can be set from a signal handler */
115 static volatile int need_to_exit;
116
117 /* ================================================== */
118
119 static void
120 handle_slew(struct timespec *raw,
121 struct timespec *cooked,
122 double dfreq,
123 double doffset,
124 LCL_ChangeType change_type,
125 void *anything);
126
127 /* ================================================== */
128
129 void
130 SCH_Initialise(void)
131 {
132 file_handlers = ARR_CreateInstance(sizeof (FileHandlerEntry));
133
134 n_timer_queue_entries = 0;
135 next_tqe_id = 0;
136
137 timer_queue.next = &timer_queue;
138 timer_queue.prev = &timer_queue;
139
140 need_to_exit = 0;
141
142 LCL_AddParameterChangeHandler(handle_slew, NULL);
143
144 LCL_ReadRawTime(&last_select_ts_raw);
145 last_select_ts = last_select_ts_raw;
146 last_select_ts_mono = 0.0;
147 last_select_ts_mono_ns = 0;
148
149 initialised = 1;
150 }
151
152
153 /* ================================================== */
154
155 void
156 SCH_Finalise(void) {
157 ARR_DestroyInstance(file_handlers);
158
159 LCL_RemoveParameterChangeHandler(handle_slew, NULL);
160
161 initialised = 0;
162 }
163
164 /* ================================================== */
165
166 void
167 SCH_AddFileHandler
168 (int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg)
169 {
170 FileHandlerEntry *ptr;
171
172 assert(initialised);
173 assert(events);
174 assert(fd >= 0);
175
176 if (fd >= FD_SETSIZE)
177 LOG_FATAL("Too many file descriptors");
178
179 /* Resize the array if the descriptor is highest so far */
180 while (ARR_GetSize(file_handlers) <= fd) {
181 ptr = ARR_GetNewElement(file_handlers);
182 ptr->handler = NULL;
183 ptr->arg = NULL;
184 ptr->events = 0;
185 }
186
187 ptr = ARR_GetElement(file_handlers, fd);
188
189 /* Don't want to allow the same fd to register a handler more than
190 once without deleting a previous association - this suggests
191 a bug somewhere else in the program. */
192 assert(!ptr->handler);
193
194 ptr->handler = handler;
195 ptr->arg = arg;
196 ptr->events = events;
197
198 if (one_highest_fd < fd + 1)
199 one_highest_fd = fd + 1;
200 }
201
202
203 /* ================================================== */
204
205 void
206 SCH_RemoveFileHandler(int fd)
207 {
208 FileHandlerEntry *ptr;
209
210 assert(initialised);
211
212 ptr = ARR_GetElement(file_handlers, fd);
213
214 /* Check that a handler was registered for the fd in question */
215 assert(ptr->handler);
216
217 ptr->handler = NULL;
218 ptr->arg = NULL;
219 ptr->events = 0;
220
221 /* Find new highest file descriptor */
222 while (one_highest_fd > 0) {
223 ptr = ARR_GetElement(file_handlers, one_highest_fd - 1);
224 if (ptr->handler)
225 break;
226 one_highest_fd--;
227 }
228 }
229
230 /* ================================================== */
231
232 void
233 SCH_SetFileHandlerEvent(int fd, int event, int enable)
234 {
235 FileHandlerEntry *ptr;
236
237 ptr = ARR_GetElement(file_handlers, fd);
238
239 if (enable)
240 ptr->events |= event;
241 else
242 ptr->events &= ~event;
243 }
244
245 /* ================================================== */
246
247 void
248 SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw)
249 {
250 if (cooked) {
251 *cooked = last_select_ts;
252 if (err)
253 *err = last_select_ts_err;
254 }
255 if (raw)
256 *raw = last_select_ts_raw;
257 }
258
259 /* ================================================== */
260
261 double
262 SCH_GetLastEventMonoTime(void)
263 {
264 return last_select_ts_mono;
265 }
266
267 /* ================================================== */
268
269 #define TQE_ALLOC_QUANTUM 32
270
271 static TimerQueueEntry *
272 allocate_tqe(void)
273 {
274 TimerQueueEntry *new_block;
275 TimerQueueEntry *result;
276 int i;
277 if (tqe_free_list == NULL) {
278 new_block = MallocArray(TimerQueueEntry, TQE_ALLOC_QUANTUM);
279 for (i=1; i<TQE_ALLOC_QUANTUM; i++) {
280 new_block[i].next = &(new_block[i-1]);
281 }
282 new_block[0].next = NULL;
283 tqe_free_list = &(new_block[TQE_ALLOC_QUANTUM - 1]);
284 }
285
286 result = tqe_free_list;
287 tqe_free_list = tqe_free_list->next;
288 return result;
289 }
290
291 /* ================================================== */
292
293 static void
294 release_tqe(TimerQueueEntry *node)
295 {
296 node->next = tqe_free_list;
297 tqe_free_list = node;
298 }
299
300 /* ================================================== */
301
302 static SCH_TimeoutID
303 get_new_tqe_id(void)
304 {
305 TimerQueueEntry *ptr;
306
307 try_again:
308 next_tqe_id++;
309 if (!next_tqe_id)
310 goto try_again;
311
312 /* Make sure the ID isn't already used */
313 for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next)
314 if (ptr->id == next_tqe_id)
315 goto try_again;
316
317 return next_tqe_id;
318 }
319
320 /* ================================================== */
321
322 SCH_TimeoutID
323 SCH_AddTimeout(struct timespec *ts, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
324 {
325 TimerQueueEntry *new_tqe;
326 TimerQueueEntry *ptr;
327
328 assert(initialised);
329
330 new_tqe = allocate_tqe();
331
332 new_tqe->id = get_new_tqe_id();
333 new_tqe->handler = handler;
334 new_tqe->arg = arg;
335 new_tqe->ts = *ts;
336 new_tqe->class = SCH_ReservedTimeoutValue;
337
338 /* Now work out where to insert the new entry in the list */
339 for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
340 if (UTI_CompareTimespecs(&new_tqe->ts, &ptr->ts) == -1) {
341 /* If the new entry comes before the current pointer location in
342 the list, we want to insert the new entry just before ptr. */
343 break;
344 }
345 }
346
347 /* At this stage, we want to insert the new entry immediately before
348 the entry identified by 'ptr' */
349
350 new_tqe->next = ptr;
351 new_tqe->prev = ptr->prev;
352 ptr->prev->next = new_tqe;
353 ptr->prev = new_tqe;
354
355 n_timer_queue_entries++;
356
357 return new_tqe->id;
358 }
359
360 /* ================================================== */
361 /* This queues a timeout to elapse at a given delta time relative to
362 the current (raw) time */
363
364 SCH_TimeoutID
365 SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
366 {
367 struct timespec now, then;
368
369 assert(initialised);
370 assert(delay >= 0.0);
371
372 LCL_ReadRawTime(&now);
373 UTI_AddDoubleToTimespec(&now, delay, &then);
374 if (UTI_CompareTimespecs(&now, &then) > 0) {
375 LOG_FATAL("Timeout overflow");
376 }
377
378 return SCH_AddTimeout(&then, handler, arg);
379
380 }
381
382 /* ================================================== */
383
384 SCH_TimeoutID
385 SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
386 SCH_TimeoutClass class,
387 SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
388 {
389 TimerQueueEntry *new_tqe;
390 TimerQueueEntry *ptr;
391 struct timespec now;
392 double diff, r;
393 double new_min_delay;
394
395 assert(initialised);
396 assert(min_delay >= 0.0);
397 assert(class < SCH_NumberOfClasses);
398
399 if (randomness > 0.0) {
400 uint32_t rnd;
401
402 UTI_GetRandomBytes(&rnd, sizeof (rnd));
403 r = rnd * (randomness / (uint32_t)-1) + 1.0;
404 min_delay *= r;
405 separation *= r;
406 }
407
408 LCL_ReadRawTime(&now);
409 new_min_delay = min_delay;
410
411 /* Check the separation from the last dispatched timeout */
412 diff = UTI_DiffTimespecsToDouble(&now, &last_class_dispatch[class]);
413 if (diff < separation && diff >= 0.0 && diff + new_min_delay < separation) {
414 new_min_delay = separation - diff;
415 }
416
417 /* Scan through list for entries in the same class and increase min_delay
418 if necessary to keep at least the separation away */
419 for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
420 if (ptr->class == class) {
421 diff = UTI_DiffTimespecsToDouble(&ptr->ts, &now);
422 if (new_min_delay > diff) {
423 if (new_min_delay - diff < separation) {
424 new_min_delay = diff + separation;
425 }
426 } else {
427 if (diff - new_min_delay < separation) {
428 new_min_delay = diff + separation;
429 }
430 }
431 }
432 }
433
434 for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
435 diff = UTI_DiffTimespecsToDouble(&ptr->ts, &now);
436 if (diff > new_min_delay) {
437 break;
438 }
439 }
440
441 /* We have located the insertion point */
442 new_tqe = allocate_tqe();
443
444 new_tqe->id = get_new_tqe_id();
445 new_tqe->handler = handler;
446 new_tqe->arg = arg;
447 UTI_AddDoubleToTimespec(&now, new_min_delay, &new_tqe->ts);
448 new_tqe->class = class;
449
450 new_tqe->next = ptr;
451 new_tqe->prev = ptr->prev;
452 ptr->prev->next = new_tqe;
453 ptr->prev = new_tqe;
454 n_timer_queue_entries++;
455
456 return new_tqe->id;
457 }
458
459 /* ================================================== */
460
461 void
462 SCH_RemoveTimeout(SCH_TimeoutID id)
463 {
464 TimerQueueEntry *ptr;
465
466 assert(initialised);
467
468 if (!id)
469 return;
470
471 for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
472
473 if (ptr->id == id) {
474 /* Found the required entry */
475
476 /* Unlink from the queue */
477 ptr->next->prev = ptr->prev;
478 ptr->prev->next = ptr->next;
479
480 /* Decrement entry count */
481 --n_timer_queue_entries;
482
483 /* Release memory back to the operating system */
484 release_tqe(ptr);
485
486 return;
487 }
488 }
489
490 /* Catch calls with invalid non-zero ID */
491 assert(0);
492 }
493
494 /* ================================================== */
495 /* Try to dispatch any timeouts that have already gone by, and
496 keep going until all are done. (The earlier ones may take so
497 long to do that the later ones come around by the time they are
498 completed). */
499
500 static void
501 dispatch_timeouts(struct timespec *now) {
502 unsigned long n_done, n_entries_on_start;
503 TimerQueueEntry *ptr;
504 SCH_TimeoutHandler handler;
505 SCH_ArbitraryArgument arg;
506
507 n_entries_on_start = n_timer_queue_entries;
508 n_done = 0;
509
510 do {
511 LCL_ReadRawTime(now);
512
513 if (!(n_timer_queue_entries > 0 &&
514 UTI_CompareTimespecs(now, &timer_queue.next->ts) >= 0)) {
515 break;
516 }
517
518 ptr = timer_queue.next;
519
520 last_class_dispatch[ptr->class] = *now;
521
522 handler = ptr->handler;
523 arg = ptr->arg;
524
525 SCH_RemoveTimeout(ptr->id);
526
527 /* Dispatch the handler */
528 (handler)(arg);
529
530 /* Increment count of timeouts handled */
531 ++n_done;
532
533 /* If the number of dispatched timeouts is significantly larger than the
534 length of the queue on start and now, assume there is a bug causing
535 an infinite loop by constantly adding a timeout with a zero or negative
536 delay. Check the actual rate of timeouts to avoid false positives in
537 case the execution slowed down so much (e.g. due to memory thrashing)
538 that it repeatedly takes more time to handle the timeout than is its
539 delay. This is a safety mechanism intended to stop a full-speed flood
540 of NTP requests due to a bug in the NTP polling. */
541
542 if (n_done > 20 &&
543 n_done > 4 * MAX(n_timer_queue_entries, n_entries_on_start) &&
544 fabs(UTI_DiffTimespecsToDouble(now, &last_select_ts_raw)) / n_done < 0.01)
545 LOG_FATAL("Possible infinite loop in scheduling");
546
547 } while (!need_to_exit);
548 }
549
550 /* ================================================== */
551
552 /* nfd is the number of bits set in all fd_sets */
553
554 static void
555 dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds)
556 {
557 FileHandlerEntry *ptr;
558 int fd;
559
560 for (fd = 0; nfd && fd < one_highest_fd; fd++) {
561 if (except_fds && FD_ISSET(fd, except_fds)) {
562 /* This descriptor has an exception, dispatch its handler */
563 ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
564 if (ptr->handler)
565 (ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
566 nfd--;
567
568 /* Don't try to read from it now */
569 if (read_fds && FD_ISSET(fd, read_fds)) {
570 FD_CLR(fd, read_fds);
571 nfd--;
572 }
573 }
574
575 if (read_fds && FD_ISSET(fd, read_fds)) {
576 /* This descriptor can be read from, dispatch its handler */
577 ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
578 if (ptr->handler)
579 (ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
580 nfd--;
581 }
582
583 if (write_fds && FD_ISSET(fd, write_fds)) {
584 /* This descriptor can be written to, dispatch its handler */
585 ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
586 if (ptr->handler)
587 (ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
588 nfd--;
589 }
590 }
591 }
592
593 /* ================================================== */
594
595 static void
596 handle_slew(struct timespec *raw,
597 struct timespec *cooked,
598 double dfreq,
599 double doffset,
600 LCL_ChangeType change_type,
601 void *anything)
602 {
603 TimerQueueEntry *ptr;
604 double delta;
605 int i;
606
607 if (change_type != LCL_ChangeAdjust) {
608 /* Make sure this handler is invoked first in order to not shift new timers
609 added from other handlers */
610 assert(LCL_IsFirstParameterChangeHandler(handle_slew));
611
612 /* If a step change occurs, just shift all raw time stamps by the offset */
613
614 for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
615 UTI_AddDoubleToTimespec(&ptr->ts, -doffset, &ptr->ts);
616 }
617
618 for (i = 0; i < SCH_NumberOfClasses; i++) {
619 UTI_AddDoubleToTimespec(&last_class_dispatch[i], -doffset, &last_class_dispatch[i]);
620 }
621
622 UTI_AddDoubleToTimespec(&last_select_ts_raw, -doffset, &last_select_ts_raw);
623 }
624
625 UTI_AdjustTimespec(&last_select_ts, cooked, &last_select_ts, &delta, dfreq, doffset);
626 }
627
628 /* ================================================== */
629
630 static void
631 fill_fd_sets(fd_set **read_fds, fd_set **write_fds, fd_set **except_fds)
632 {
633 FileHandlerEntry *handlers;
634 fd_set *rd, *wr, *ex;
635 int i, n, events;
636
637 n = ARR_GetSize(file_handlers);
638 handlers = ARR_GetElements(file_handlers);
639 rd = wr = ex = NULL;
640
641 for (i = 0; i < n; i++) {
642 events = handlers[i].events;
643
644 if (!events)
645 continue;
646
647 if (events & SCH_FILE_INPUT) {
648 if (!rd) {
649 rd = *read_fds;
650 FD_ZERO(rd);
651 }
652 FD_SET(i, rd);
653 }
654
655 if (events & SCH_FILE_OUTPUT) {
656 if (!wr) {
657 wr = *write_fds;
658 FD_ZERO(wr);
659 }
660 FD_SET(i, wr);
661 }
662
663 if (events & SCH_FILE_EXCEPTION) {
664 if (!ex) {
665 ex = *except_fds;
666 FD_ZERO(ex);
667 }
668 FD_SET(i, ex);
669 }
670 }
671
672 if (!rd)
673 *read_fds = NULL;
674 if (!wr)
675 *write_fds = NULL;
676 if (!ex)
677 *except_fds = NULL;
678 }
679
680 /* ================================================== */
681
682 #define JUMP_DETECT_THRESHOLD 10
683
684 static int
685 check_current_time(struct timespec *prev_raw, struct timespec *raw, int timeout,
686 struct timeval *orig_select_tv,
687 struct timeval *rem_select_tv)
688 {
689 struct timespec elapsed_min, elapsed_max, orig_select_ts, rem_select_ts;
690 double step, elapsed;
691
692 UTI_TimevalToTimespec(orig_select_tv, &orig_select_ts);
693
694 /* Get an estimate of the time spent waiting in the select() call. On some
695 systems (e.g. Linux) the timeout timeval is modified to return the
696 remaining time, use that information. */
697 if (timeout) {
698 elapsed_max = elapsed_min = orig_select_ts;
699 } else if (rem_select_tv && rem_select_tv->tv_sec >= 0 &&
700 rem_select_tv->tv_sec <= orig_select_tv->tv_sec &&
701 (rem_select_tv->tv_sec != orig_select_tv->tv_sec ||
702 rem_select_tv->tv_usec != orig_select_tv->tv_usec)) {
703 UTI_TimevalToTimespec(rem_select_tv, &rem_select_ts);
704 UTI_DiffTimespecs(&elapsed_min, &orig_select_ts, &rem_select_ts);
705 elapsed_max = elapsed_min;
706 } else {
707 if (rem_select_tv)
708 elapsed_max = orig_select_ts;
709 else
710 UTI_DiffTimespecs(&elapsed_max, raw, prev_raw);
711 UTI_ZeroTimespec(&elapsed_min);
712 }
713
714 if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec >
715 raw->tv_sec + JUMP_DETECT_THRESHOLD) {
716 LOG(LOGS_WARN, "Backward time jump detected!");
717 } else if (prev_raw->tv_sec + elapsed_max.tv_sec + JUMP_DETECT_THRESHOLD <
718 raw->tv_sec) {
719 LOG(LOGS_WARN, "Forward time jump detected!");
720 } else {
721 return 1;
722 }
723
724 step = UTI_DiffTimespecsToDouble(&last_select_ts_raw, raw);
725 elapsed = UTI_TimespecToDouble(&elapsed_min);
726 step += elapsed;
727
728 /* Cooked time may no longer be valid after dispatching the handlers */
729 LCL_NotifyExternalTimeStep(raw, raw, step, fabs(step));
730
731 return 0;
732 }
733
734 /* ================================================== */
735
736 static void
737 update_monotonic_time(struct timespec *now, struct timespec *before)
738 {
739 struct timespec diff;
740
741 /* Avoid frequent floating-point operations and handle small
742 increments to a large value */
743
744 UTI_DiffTimespecs(&diff, now, before);
745 if (diff.tv_sec == 0) {
746 last_select_ts_mono_ns += diff.tv_nsec;
747 } else {
748 last_select_ts_mono += fabs(UTI_TimespecToDouble(&diff) +
749 last_select_ts_mono_ns / 1.0e9);
750 last_select_ts_mono_ns = 0;
751 }
752
753 if (last_select_ts_mono_ns > TS_MONO_PRECISION_NS) {
754 last_select_ts_mono += last_select_ts_mono_ns / 1.0e9;
755 last_select_ts_mono_ns = 0;
756 }
757 }
758
759 /* ================================================== */
760
761 void
762 SCH_MainLoop(void)
763 {
764 fd_set read_fds, write_fds, except_fds;
765 fd_set *p_read_fds, *p_write_fds, *p_except_fds;
766 int status, errsv;
767 struct timeval tv, saved_tv, *ptv;
768 struct timespec ts, now, saved_now, cooked;
769 double err;
770
771 assert(initialised);
772
773 while (!need_to_exit) {
774 /* Dispatch timeouts and fill now with current raw time */
775 dispatch_timeouts(&now);
776 saved_now = now;
777
778 /* The timeout handlers may request quit */
779 if (need_to_exit)
780 break;
781
782 /* Check whether there is a timeout and set it up */
783 if (n_timer_queue_entries > 0) {
784 UTI_DiffTimespecs(&ts, &timer_queue.next->ts, &now);
785 assert(ts.tv_sec > 0 || ts.tv_nsec > 0);
786
787 UTI_TimespecToTimeval(&ts, &tv);
788 ptv = &tv;
789 saved_tv = tv;
790 } else {
791 ptv = NULL;
792 saved_tv.tv_sec = saved_tv.tv_usec = 0;
793 }
794
795 p_read_fds = &read_fds;
796 p_write_fds = &write_fds;
797 p_except_fds = &except_fds;
798 fill_fd_sets(&p_read_fds, &p_write_fds, &p_except_fds);
799
800 /* if there are no file descriptors being waited on and no
801 timeout set, this is clearly ridiculous, so stop the run */
802 if (!ptv && !p_read_fds && !p_write_fds)
803 LOG_FATAL("Nothing to do");
804
805 status = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);
806 errsv = errno;
807
808 LCL_ReadRawTime(&now);
809 LCL_CookTime(&now, &cooked, &err);
810
811 update_monotonic_time(&now, &last_select_ts_raw);
812
813 /* Check if the time didn't jump unexpectedly */
814 if (!check_current_time(&saved_now, &now, status == 0, &saved_tv, ptv)) {
815 /* Cook the time again after handling the step */
816 LCL_CookTime(&now, &cooked, &err);
817 }
818
819 last_select_ts_raw = now;
820 last_select_ts = cooked;
821 last_select_ts_err = err;
822
823 if (status < 0) {
824 if (!need_to_exit && errsv != EINTR) {
825 LOG_FATAL("select() failed : %s", strerror(errsv));
826 }
827 } else if (status > 0) {
828 /* A file descriptor is ready for input or output */
829 dispatch_filehandlers(status, p_read_fds, p_write_fds, p_except_fds);
830 } else {
831 /* No descriptors readable, timeout must have elapsed.
832 Therefore, tv must be non-null */
833 assert(ptv);
834
835 /* There's nothing to do here, since the timeouts
836 will be dispatched at the top of the next loop
837 cycle */
838
839 }
840 }
841 }
842
843 /* ================================================== */
844
845 void
846 SCH_QuitProgram(void)
847 {
848 need_to_exit = 1;
849 }
850
851 /* ================================================== */
852