NTP_ADJTIME(2)

πŸ’– NTP_ADJTIME β€” the system calls that let the NTP daemon vibe-check your clock πŸ’–

okay bestie, welcome to ntp_adjtime(2) and ntp_gettime(2) β€” the Network Time Protocol (NTP) daemon interface system calls, living their best life in section 2 of the System Calls Manual. πŸ•°οΈβœ¨


🌸 LIBRARY 🌸

Standard C Library (libc, -lc). she's classic, she's everywhere, she's giving main character energy fr fr πŸ’…


✨ SYNOPSIS ✨

#include <sys/timex.h>

int
ntp_adjtime(struct timex *);

int
ntp_gettime(struct ntptimeval *);

πŸ¦‹ DESCRIPTION πŸ¦‹

so. these two system calls β€” ntp_adjtime() and ntp_gettime() β€” are literally the kernel interface to the Network Time Protocol (NTP) daemon, ntpd(8). that's the whole vibe. the kernel and the NTP daemon are texting through these syscalls and honestly? iconic. πŸ“±

ntp_adjtime() is what the NTP daemon uses to adjust the system clock to some externally derived time. and the time offset + related variables that ntp_adjtime() sets? those get used by hardclock() to adjust the phase and frequency of the phase-lock loop (PLL) or frequency-lock loop (FLL) β€” whichever one is controlling your system clock. no cap, this is the machinery. πŸ”§

ntp_gettime() is the friendly one β€” it hands the time, the maximum error (sync distance), and the estimated error (dispersion) over to client user application programs. so generous of her. 🫢

heads up bestie: everything below that refers to PPS is ONLY relevant if the PPS_SYNC option is enabled in the kernel. otherwise it's just decorative lore. πŸ’€

ntp_adjtime() takes as its argument a struct timex *, shaped exactly like this:

struct timex {
        unsigned int modes;     /* clock mode bits (wo) */
        long offset;            /* time offset (us) (rw) */
        long freq;              /* frequency offset (scaled ppm) (rw) */
        long maxerror;          /* maximum error (us) (rw) */
        long esterror;          /* estimated error (us) (rw) */
        int status;             /* clock status bits (rw) */
        long constant;          /* pll time constant (rw) */
        long precision;         /* clock precision (us) (ro) */
        long tolerance;         /* clock frequency tolerance (scaled
                                 * ppm) (ro) */
        /*
         * The following read-only structure members are implemented
         * only if the PPS signal discipline is configured in the
         * kernel.
         */
        long ppsfreq;           /* pps frequency (scaled ppm) (ro) */
        long jitter;            /* pps jitter (us) (ro) */
        int shift;              /* interval duration (s) (shift) (ro) */
        long stabil;            /* pps stability (scaled ppm) (ro) */
        long jitcnt;            /* jitter limit exceeded (ro) */
        long calcnt;            /* calibration intervals (ro) */
        long errcnt;            /* calibration errors (ro) */
        long stbcnt;            /* stability limit exceeded (ro) */
};

πŸŽ€ the members, explained with love πŸŽ€

here's what each member of that struct actually means when it's used as the argument for ntp_adjtime():

  • modes β€” defines what settings should be changed with the current ntp_adjtime() call (write-only). it's a bitwise OR of these:
    • MOD_OFFSET β€” set time offset
    • MOD_FREQUENCY β€” set frequency offset
    • MOD_MAXERROR β€” set maximum time error
    • MOD_ESTERROR β€” set estimated time error
    • MOD_STATUS β€” set clock status bits
    • MOD_TIMECONST β€” set PLL time constant
    • MOD_CLKA β€” set clock A
    • MOD_CLKB β€” set clock B
  • offset β€” time offset (in microseconds), used by the PLL/FLL to nudge the system time along in small increments (read-write).
  • freq β€” frequency offset (scaled ppm) (read-write).
  • maxerror β€” maximum error (in microseconds). it gets initialized by an ntp_adjtime() call, and then the kernel bumps it up once each second to reflect the maximum error bound growing (read-write).
  • esterror β€” estimated error (in microseconds). set and read by ntp_adjtime(), but the kernel literally doesn't use it (read-write). she's there for the plot.
  • status β€” system clock status bits (read-write). bitwise OR of these:
    • STA_PLL β€” Enable PLL updates (read-write).
    • STA_PPSFREQ β€” Enable PPS freq discipline (read-write).
    • STA_PPSTIME β€” Enable PPS time discipline (read-write).
    • STA_FLL β€” Select frequency-lock mode (read-write).
    • STA_INS β€” Insert leap (read-write).
    • STA_DEL β€” Delete leap (read-write).
    • STA_UNSYNC β€” Clock unsynchronized (read-write).
    • STA_FREQHOLD β€” Hold frequency (read-write).
    • STA_PPSSIGNAL β€” PPS signal present (read-only).
    • STA_PPSJITTER β€” PPS signal jitter exceeded (read-only).
    • STA_PPSWANDER β€” PPS signal wander exceeded (read-only).
    • STA_PPSERROR β€” PPS signal calibration error (read-only).
    • STA_CLOCKERR β€” Clock hardware fault (read-only).
  • constant β€” PLL time constant. this determines the bandwidth, or β€œstiffness”, of the PLL (read-write).
  • precision β€” clock precision (in microseconds). in most cases it's the same as the kernel tick variable (peep hz(9)). if a precision clock counter or external time-keeping signal is available, it could be much lower β€” and it could depend on the state of the signal (read-only).
  • tolerance β€” maximum frequency error, aka the tolerance of the CPU clock oscillator (scaled ppm). ordinarily it's a property of the architecture, but it could change under the influence of external time-keeping signals (read-only).
  • ppsfreq β€” PPS frequency offset produced by the frequency median filter (scaled ppm) (read-only).
  • jitter β€” PPS jitter measured by the time median filter, in microseconds (read-only).
  • shift β€” logarithm to base 2 of the interval duration in seconds (PPS, read-only).
  • stabil β€” PPS stability (scaled ppm); dispersion (wander) measured by the frequency median filter (read-only).
  • jitcnt β€” number of seconds that have been discarded because the jitter measured by the time median filter exceeded the limit MAXTIME (PPS, read-only).
  • calcnt β€” count of calibration intervals (PPS, read-only).
  • errcnt β€” number of calibration intervals that have been discarded because the wander exceeded the limit MAXFREQ, or where the calibration interval jitter exceeded two ticks (PPS, read-only).
  • stbcnt β€” number of calibration intervals that have been discarded because the frequency wander exceeded the limit MAXFREQ/4 (PPS, read-only).

and after the ntp_adjtime() call goes through, the struct timex * structure holds the current values of the corresponding variables. she updates herself, periodt. ✨


ntp_gettime() takes as its argument a struct ntptimeval * with these members:

struct ntptimeval {
        struct timeval time;    /* current time (ro) */
        long maxerror;          /* maximum error (us) (ro) */
        long esterror;          /* estimated error (us) (ro) */
};

and here's what they mean:

  • time β€” current time (read-only).
  • maxerror β€” maximum error in microseconds (read-only).
  • esterror β€” estimated error in microseconds (read-only).

πŸ”₯ RETURN VALUES πŸ”₯

ntp_adjtime() and ntp_gettime() return the current state of the clock on success β€” or any of the errors of copyin(9) and copyout(9). and ntp_adjtime() may additionally return EPERM if the user calling ntp_adjtime() doesn't have sufficient permissions. rude but valid. πŸ™ˆ

possible states of the clock are:

  • TIME_OK β€” Everything okay, no leap second warning. she's thriving. πŸ’–
  • TIME_INS β€” the β€œinsert leap second” warning. at the end of the day, a leap second will be inserted after 23:59:59.
  • TIME_DEL β€” the β€œdelete leap second” warning. at the end of the day, second 23:59:59 will be skipped. bye girl πŸ’€
  • TIME_OOP β€” leap second in progress.
  • TIME_WAIT β€” leap second has occurred within the last few seconds.
  • TIME_ERROR β€” clock not synchronized.

πŸ’€ ERRORS πŸ’€

the ntp_adjtime() system call may return EPERM if the caller does not have sufficient permissions. that's it, that's the list. know your place bestie. πŸ‘‘


🌈 SEE ALSO 🌈


🚨 BUGS 🚨

take note, bestie: this API is extremely complex and stateful. users should not attempt modification without first reviewing the ntpd(8) sources in depth. like, in depth depth. do not freestyle this one, i am begging. 😭

and that's the whole page, no notes. this little slice of the System Calls Manual is brought to you by the good people at Debian, dated July 13, 2005 β€” yeah she's an elder but she still ate πŸ’…βœ¨ stay synced bestie, mwah πŸŽ€πŸ¦‹πŸŒΈ

πŸ“¦ where this page comes from