top: add keyboard commands to --help
[oweals/busybox.git] / networking / ntpd.c
index 95dfdb1197e4b699fc6bde9bc23e7b9269441ab3..8fe529edbb878c6ab8a9a785654ec1a306faebb7 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Author: Adam Tkac <vonsch@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  *
  * Parts of OpenNTPD clock syncronization code is replaced by
  * code which is based on ntp-4.2.6, whuch carries the following
@@ -49,7 +49,7 @@
 /* High-level description of the algorithm:
  *
  * We start running with very small poll_exp, BURSTPOLL,
- * in order to quickly accumulate INITIAL_SAMLPES datapoints
+ * in order to quickly accumulate INITIAL_SAMPLES datapoints
  * for each peer. Then, time is stepped if the offset is larger
  * than STEP_THRESHOLD, otherwise it isn't; anyway, we enlarge
  * poll_exp to MINPOLL and enter frequency measurement step:
@@ -77,7 +77,7 @@
 
 #define RETRY_INTERVAL  5       /* on error, retry in N secs */
 #define RESPONSE_INTERVAL 15    /* wait for reply up to N secs */
-#define INITIAL_SAMLPES 4       /* how many samples do we want for init */
+#define INITIAL_SAMPLES 4       /* how many samples do we want for init */
 
 /* Clock discipline parameters and constants */
 
@@ -89,7 +89,7 @@
 //UNUSED: #define PANIC_THRESHOLD 1000    /* panic threshold (sec) */
 
 #define FREQ_TOLERANCE  0.000015 /* frequency tolerance (15 PPM) */
-#define BURSTPOLL       0      /* initial poll */
+#define BURSTPOLL       0       /* initial poll */
 #define MINPOLL         5       /* minimum poll interval. std ntpd uses 6 (6: 64 sec) */
 #define BIGPOLL         10      /* drop to lower poll at any trouble (10: 17 min) */
 #define MAXPOLL         12      /* maximum poll interval (12: 1.1h, 17: 36.4h). std ntpd uses 17 */
@@ -592,7 +592,6 @@ filter_datapoints(peer_t *p)
                        p->filter_offset, x,
                        p->filter_dispersion,
                        p->filter_jitter);
-
 }
 
 static void
@@ -603,9 +602,9 @@ reset_peer_stats(peer_t *p, double offset)
 
        for (i = 0; i < NUM_DATAPOINTS; i++) {
                if (small_ofs) {
-                       p->filter_datapoint[i].d_recv_time -= offset;
+                       p->filter_datapoint[i].d_recv_time += offset;
                        if (p->filter_datapoint[i].d_offset != 0) {
-                               p->filter_datapoint[i].d_offset -= offset;
+                               p->filter_datapoint[i].d_offset += offset;
                        }
                } else {
                        p->filter_datapoint[i].d_recv_time  = G.cur_time;
@@ -614,13 +613,12 @@ reset_peer_stats(peer_t *p, double offset)
                }
        }
        if (small_ofs) {
-               p->lastpkt_recv_time -= offset;
+               p->lastpkt_recv_time += offset;
        } else {
                p->reachable_bits = 0;
                p->lastpkt_recv_time = G.cur_time;
        }
        filter_datapoints(p); /* recalc p->filter_xxx */
-       p->next_action_time -= offset;
        VERB5 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time);
 }
 
@@ -735,6 +733,13 @@ send_query_to_peer(peer_t *p)
 }
 
 
+/* Note that there is no provision to prevent several run_scripts
+ * to be done in quick succession. In fact, it happens rather often
+ * if initial syncronization results in a step.
+ * You will see "step" and then "stratum" script runs, sometimes
+ * as close as only 0.002 seconds apart.
+ * Script should be ready to deal with this.
+ */
 static void run_script(const char *action, double offset)
 {
        char *argv[3];
@@ -765,7 +770,7 @@ static void run_script(const char *action, double offset)
 
        /* Don't want to wait: it may run hwclock --systohc, and that
         * may take some time (seconds): */
-       /*wait4pid(spawn(argv));*/
+       /*spawn_and_wait(argv);*/
        spawn(argv);
 
        unsetenv("stratum");
@@ -808,11 +813,14 @@ step_time(double offset)
        for (item = G.ntp_peers; item != NULL; item = item->link) {
                peer_t *pp = (peer_t *) item->data;
                reset_peer_stats(pp, offset);
+               //bb_error_msg("offset:%f pp->next_action_time:%f -> %f",
+               //      offset, pp->next_action_time, pp->next_action_time + offset);
+               pp->next_action_time += offset;
        }
        /* Globals: */
-       G.cur_time -= offset;
-       G.last_update_recv_time -= offset;
-       G.last_script_run -= offset;
+       G.cur_time += offset;
+       G.last_update_recv_time += offset;
+       G.last_script_run += offset;
 }
 
 
@@ -857,7 +865,7 @@ fit(peer_t *p, double rd)
                VERB3 bb_error_msg("peer %s unfit for selection: unreachable", p->p_dotted);
                return 0;
        }
-#if 0  /* we filter out such packets earlier */
+#if 0 /* we filter out such packets earlier */
        if ((p->lastpkt_status & LI_ALARM) == LI_ALARM
         || p->lastpkt_stratum >= MAXSTRAT
        ) {
@@ -1185,8 +1193,8 @@ update_local_clock(peer_t *p)
        abs_offset = fabs(offset);
 
 #if 0
-       /* If needed, -S script can detect this by looking at $offset
-        * env var and kill parent */
+       /* If needed, -S script can do it by looking at $offset
+        * env var and killing parent */
        /* If the offset is too large, give up and go home */
        if (abs_offset > PANIC_THRESHOLD) {
                bb_error_msg_and_die("offset %f far too big, exiting", offset);
@@ -1757,6 +1765,10 @@ recv_and_process_client_pkt(void /*int fd*/)
        /* this time was obtained between poll() and recv() */
        msg.m_rectime = d_to_lfp(G.cur_time);
        msg.m_xmttime = d_to_lfp(gettime1900d()); /* this instant */
+       if (G.peer_cnt == 0) {
+               /* we have no peers: "stratum 1 server" mode. reftime = our own time */
+               G.reftime = G.cur_time;
+       }
        msg.m_reftime = d_to_lfp(G.reftime);
        msg.m_orgtime = query_xmttime;
        msg.m_rootdelay = d_to_sfp(G.rootdelay);
@@ -1894,8 +1906,13 @@ static NOINLINE void ntp_init(char **argv)
                bb_show_usage();
 //     if (opts & OPT_x) /* disable stepping, only slew is allowed */
 //             G.time_was_stepped = 1;
-       while (peers)
-               add_peers(llist_pop(&peers));
+       if (peers) {
+               while (peers)
+                       add_peers(llist_pop(&peers));
+       } else {
+               /* -l but no peers: "stratum 1 server" mode */
+               G.stratum = 1;
+       }
        if (!(opts & OPT_n)) {
                bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO, argv);
                logmode = LOGMODE_NONE;
@@ -1912,9 +1929,28 @@ static NOINLINE void ntp_init(char **argv)
        if (opts & OPT_N)
                setpriority(PRIO_PROCESS, 0, -15);
 
-       bb_signals((1 << SIGTERM) | (1 << SIGINT), record_signo);
-       /* Removed SIGHUP here: */
-       bb_signals((1 << SIGPIPE) | (1 << SIGCHLD), SIG_IGN);
+       /* If network is up, syncronization occurs in ~10 seconds.
+        * We give "ntpd -q" a full minute to finish, then we exit.
+        *
+        * I tested ntpd 4.2.6p1 and apparently it never exits
+        * (will try forever), but it does not feel right.
+        * The goal of -q is to act like ntpdate: set time
+        * after a reasonably small period of polling, or fail.
+        */
+       if (opts & OPT_q)
+               alarm(60);
+
+       bb_signals(0
+               | (1 << SIGTERM)
+               | (1 << SIGINT)
+               | (1 << SIGALRM)
+               , record_signo
+       );
+       bb_signals(0
+               | (1 << SIGPIPE)
+               | (1 << SIGCHLD)
+               , SIG_IGN
+       );
 }
 
 int ntpd_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -1936,14 +1972,14 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
        idx2peer = xzalloc(sizeof(idx2peer[0]) * cnt);
        pfd = xzalloc(sizeof(pfd[0]) * cnt);
 
-       /* Countdown: we never sync before we sent INITIAL_SAMLPES+1
+       /* Countdown: we never sync before we sent INITIAL_SAMPLES+1
         * packets to each peer.
         * NB: if some peer is not responding, we may end up sending
         * fewer packets to it and more to other peers.
-        * NB2: sync usually happens using INITIAL_SAMLPES packets,
+        * NB2: sync usually happens using INITIAL_SAMPLES packets,
         * since last reply does not come back instantaneously.
         */
-       cnt = G.peer_cnt * (INITIAL_SAMLPES + 1);
+       cnt = G.peer_cnt * (INITIAL_SAMPLES + 1);
 
        while (!bb_got_signal) {
                llist_t *item;
@@ -2007,7 +2043,7 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
                nfds = poll(pfd, i, timeout * 1000);
                gettime1900d(); /* sets G.cur_time */
                if (nfds <= 0) {
-                       if (G.cur_time - G.last_script_run > 11*60) {
+                       if (G.script_name && G.cur_time - G.last_script_run > 11*60) {
                                /* Useful for updating battery-backed RTC and such */
                                run_script("periodic", G.last_update_offset);
                                gettime1900d(); /* sets G.cur_time */
@@ -2052,7 +2088,6 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
 static double
 direct_freq(double fp_offset)
 {
-
 #ifdef KERNEL_PLL
        /*
         * If the kernel is enabled, we need the residual offset to
@@ -2075,7 +2110,7 @@ direct_freq(double fp_offset)
 }
 
 static void
-set_freq(double        freq) /* frequency update */
+set_freq(double freq) /* frequency update */
 {
        char tbuf[80];