static struct conf_entry * parse_source (struct opts *opts, struct conf_entry *conf) { char *host = NULL; char *port = NULL; char *proxy = NULL; /* a source entry: * source * host <host> * port <port> * [proxy <proxy>] * end */ assert (!strcmp (conf->key, "source")); conf = conf->next; while (conf && strcmp (conf->key, "end")) { if (!strcmp (conf->key, "host")) host = conf->value; else if (!strcmp (conf->key, "port")) port = conf->value; else if (!strcmp (conf->key, "proxy")) proxy = conf->value; else fatal ("malformed config: '%s' in source stanza", conf->key); conf = conf->next; } if (!conf) fatal ("unclosed source stanza"); if (!host || !port) fatal ("incomplete source stanza (needs host, port)"); add_source_to_conf (opts, host, port, proxy); return conf; }
int API main (int argc, char *argv[], char *envp[]) { initalize_syslog (); struct state state; /* TODO(wad) EVENT_BASE_FLAG_PRECISE_TIMER | EVENT_BASE_FLAG_PRECISE_TIMER */ struct event_base *base = event_base_new(); if (!base) { fatal ("could not allocated new event base"); } /* Add three priority levels: * 0 - time saving. Must be done before any other events are handled. * 1 - network synchronization events * 2 - any other events (wake, platform, etc) */ event_base_priority_init (base, MAX_EVENT_PRIORITIES); memset (&state, 0, sizeof (state)); set_conf_defaults (&state.opts); parse_argv (&state.opts, argc, argv); check_conf (&state); load_conf (&state.opts); check_conf (&state); if (!state.opts.sources) add_source_to_conf (&state.opts, DEFAULT_HOST, DEFAULT_PORT, DEFAULT_PROXY); state.base = base; state.envp = envp; state.backoff = state.opts.wait_between_tries; /* TODO(wad) move this into setup_time_setter */ /* grab a handle to /dev/rtc for time-setter. */ if (state.opts.should_sync_hwclock && platform->rtc_open(&state.hwclock)) { pinfo ("can't open hwclock fd"); state.opts.should_sync_hwclock = 0; } /* install the SIGCHLD handler for the setter and tlsdate */ if (setup_sigchld_event (&state, 1)) { error ("Failed to setup SIGCHLD event"); goto out; } /* fork off the privileged helper */ info ("spawning time setting helper . . ."); if (setup_time_setter (&state)) { error ("could not fork privileged coprocess"); goto out; } /* release the hwclock now that the time-setter is running. */ if (state.opts.should_sync_hwclock) { platform->rtc_close (&state.hwclock); } /* drop privileges before touching any untrusted data */ drop_privs_to (state.opts.user, state.opts.group); /* register a signal handler to save time at shutdown */ if (state.opts.should_save_disk) { struct event *event = event_new (base, SIGTERM, EV_SIGNAL|EV_PERSIST, action_sigterm, &state); if (!event) fatal ("Failed to create SIGTERM event"); event_priority_set (event, PRI_SAVE); event_add (event, NULL); } if (state.opts.should_dbus && init_dbus (&state)) { error ("Failed to initialize DBus"); goto out; } /* Register the tlsdate event before any listeners could show up. */ state.events[E_TLSDATE] = event_new (base, -1, EV_TIMEOUT, action_run_tlsdate, &state); if (!state.events[E_TLSDATE]) { error ("Failed to create tlsdate event"); goto out; } event_priority_set (state.events[E_TLSDATE], PRI_NET); /* The timeout and fd will be filled in per-call. */ if (setup_tlsdate_status (&state)) { error ("Failed to create tlsdate status event"); goto out; } /* TODO(wad) Could use a timeout on this to catch setter death? */ /* EV_READ is for truncation/EPIPE notification */ state.events[E_SAVE] = event_new (base, state.setter_save_fd, EV_READ|EV_WRITE, action_sync_and_save, &state); if (!state.events[E_SAVE]) { error ("Failed to create sync & save event"); goto out; } event_priority_set (state.events[E_SAVE], PRI_SAVE); /* Start by grabbing the system time. */ state.last_sync_type = SYNC_TYPE_RTC; state.last_time = time (NULL); /* If possible, grab disk time and check the two. */ if (state.opts.should_load_disk) { time_t disk_time = state.last_time; if (!load_disk_timestamp (state.timestamp_path, &disk_time)) { info ("disk timestamp available: yes"); if (!is_sane_time (state.last_time) || state.last_time < disk_time) { state.last_sync_type = SYNC_TYPE_DISK; state.last_time = disk_time; } } else { info ("disk timestamp available: no"); } } if (!is_sane_time (state.last_time)) { state.last_sync_type = SYNC_TYPE_BUILD; state.last_time = RECENT_COMPILE_DATE + 1; } /* Save and announce the initial time source. */ trigger_event (&state, E_SAVE, -1); info ("initial time sync type: %s", sync_type_str (state.last_sync_type)); /* Initialize platform specific loop behavior */ if (platform_init_cros (&state)) { error ("Failed to initialize platform code"); goto out; } if (setup_event_route_up (&state)) { error ("Failed to setup route up monitoring"); goto out; } if (setup_event_timer_sync (&state)) { error ("Failed to setup a timer event"); goto out; } if (setup_event_timer_continuity (&state)) { error ("Failed to setup continuity timer"); goto out; } /* Add a forced sync event to the event list. */ action_kickoff_time_sync (-1, EV_TIMEOUT, &state); info ("Entering dispatch . . ."); event_base_dispatch (base); info ("tlsdated terminating gracefully"); out: return cleanup_main (&state); }
int API main (int argc, char *argv[], char *envp[]) { struct routeup rtc; int hwclock_fd = -1; time_t last_success = 0; struct opts opts; int wait_time = 0; set_conf_defaults(&opts); parse_argv(&opts, argc, argv); check_conf(&opts); load_conf(&opts); check_conf(&opts); if (!opts.sources) add_source_to_conf(&opts, DEFAULT_HOST, DEFAULT_PORT, DEFAULT_PROXY); /* grab a handle to /dev/rtc for sync_hwclock() */ if (opts.should_sync_hwclock && (hwclock_fd = open (DEFAULT_RTC_DEVICE, O_RDONLY)) < 0) { pinfo ("can't open hwclock fd"); opts.should_sync_hwclock = 0; } /* set up a netlink context if we need one */ if (opts.should_netlink && routeup_setup (&rtc)) pfatal ("Can't open netlink socket"); if (!is_sane_time (time (NULL))) { struct timeval tv = { 0, 0 }; /* * If the time is before the build timestamp, we're probably on * a system with a broken rtc. Try loading the timestamp off * disk. */ tv.tv_sec = RECENT_COMPILE_DATE; if (opts.should_load_disk && load_disk_timestamp (timestamp_path, &tv.tv_sec)) pinfo ("can't load disk timestamp"); if (!opts.dry_run && settimeofday (&tv, NULL)) pfatal ("settimeofday() failed"); dbus_announce(); /* * don't save here - we either just loaded this time or used the * default time, and neither of those are good to save */ sync_and_save (hwclock_fd, opts.should_sync_hwclock, 0); } /* register a signal handler to save time at shutdown */ if (opts.should_save_disk) signal (SIGTERM, sigterm_handler); /* * Try once right away. If we fail, wait for a route to appear, then try * for a while; repeat whenever another route appears. Try until we * succeed. */ if (!tlsdate (&opts, envp)) { last_success = time (NULL); sync_and_save (hwclock_fd, opts.should_sync_hwclock, opts.should_save_disk); dbus_announce(); } /* * Loop until we catch a fatal signal or routeup_once() fails. We run * tlsdate at least once a day, but possibly as often as routes come up; * this should handle cases like a VPN being brought up and down * periodically. */ wait_time = add_jitter(opts.steady_state_interval, opts.jitter); while (wait_for_event (&rtc, opts.should_netlink, wait_time) >= 0) { /* * If a route just came up, run tlsdate; if it * succeeded, then we're good and can keep time locally * from now on. */ int i; int backoff = opts.wait_between_tries; wait_time = add_jitter(opts.steady_state_interval, opts.jitter); if (time (NULL) - last_success < opts.min_steady_state_interval) continue; for (i = 0; i < opts.max_tries && tlsdate (&opts, envp); ++i) { if (backoff < 1) fatal ("backoff too small? %d", backoff); sleep (backoff); if (backoff < MAX_SANE_BACKOFF) backoff *= 2; } if (i != opts.max_tries) { last_success = time (NULL); info ("tlsdate succeeded"); sync_and_save (hwclock_fd, opts.should_sync_hwclock, opts.should_save_disk); dbus_announce(); } } return 1; }