1*eabc0478Schristos /* $NetBSD: refclock_gpsdjson.c,v 1.14 2024/08/18 20:47:18 christos Exp $ */ 2b8ecfcfeSchristos 3b8ecfcfeSchristos /* 4b8ecfcfeSchristos * refclock_gpsdjson.c - clock driver as GPSD JSON client 5b8ecfcfeSchristos * Juergen Perlinger (perlinger@ntp.org) 6b8ecfcfeSchristos * Feb 11, 2014 for the NTP project. 7b8ecfcfeSchristos * The contents of 'html/copyright.html' apply. 8b8ecfcfeSchristos * 9b8ecfcfeSchristos * Heavily inspired by refclock_nmea.c 10b8ecfcfeSchristos * 115d681e99Schristos * Special thanks to Gary Miller and Hal Murray for their comments and 125d681e99Schristos * ideas. 135d681e99Schristos * 14b8ecfcfeSchristos * Note: This will currently NOT work with Windows due to some 15b8ecfcfeSchristos * limitations: 16b8ecfcfeSchristos * 17b8ecfcfeSchristos * - There is no GPSD for Windows. (There is an unofficial port to 18b8ecfcfeSchristos * cygwin, but Windows is not officially supported.) 19b8ecfcfeSchristos * 205d681e99Schristos * - To work properly, this driver needs PPS and TPV/TOFF sentences 215d681e99Schristos * from GPSD. I don't see how the cygwin port should deal with the 225d681e99Schristos * PPS signal. 23b8ecfcfeSchristos * 24b8ecfcfeSchristos * - The device name matching must be done in a different way for 25b8ecfcfeSchristos * Windows. (Can be done with COMxx matching, as done for NMEA.) 26b8ecfcfeSchristos * 27b8ecfcfeSchristos * Apart from those minor hickups, once GPSD has been fully ported to 285d681e99Schristos * Windows, there's no reason why this should not work there ;-) If this 295d681e99Schristos * is ever to happen at all is a different question. 305d681e99Schristos * 315d681e99Schristos * --------------------------------------------------------------------- 325d681e99Schristos * 335d681e99Schristos * This driver works slightly different from most others, as the PPS 345d681e99Schristos * information (if available) is also coming from GPSD via the data 355d681e99Schristos * connection. This makes using both the PPS data and the serial data 365d681e99Schristos * easier, but OTOH it's not possible to use the ATOM driver to feed a 375d681e99Schristos * raw PPS stream to the core of NTPD. 385d681e99Schristos * 395d681e99Schristos * To go around this, the driver can use a secondary clock unit 405d681e99Schristos * (units>=128) that operate in tandem with the primary clock unit 415d681e99Schristos * (unit%128). The primary clock unit does all the IO stuff and data 425d681e99Schristos * decoding; if a a secondary unit is attached to a primary unit, this 435d681e99Schristos * secondary unit is feed with the PPS samples only and can act as a PPS 445d681e99Schristos * source to the clock selection. 455d681e99Schristos * 465d681e99Schristos * The drawback is that the primary unit must be present for the 475d681e99Schristos * secondary unit to work. 485d681e99Schristos * 495d681e99Schristos * This design is a compromise to reduce the IO load for both NTPD and 505d681e99Schristos * GPSD; it also ensures that data is transmitted and evaluated only 515d681e99Schristos * once on the side of NTPD. 525d681e99Schristos * 535d681e99Schristos * --------------------------------------------------------------------- 545d681e99Schristos * 555d681e99Schristos * trouble shooting hints: 565d681e99Schristos * 575d681e99Schristos * Enable and check the clock stats. Check if there are bad replies; 585d681e99Schristos * there should be none. If there are actually bad replies, then the 595d681e99Schristos * driver cannot parse all JSON records from GPSD, and some record 605d681e99Schristos * types are vital for the operation of the driver. This indicates a 615d681e99Schristos * problem on the protocol level. 625d681e99Schristos * 635d681e99Schristos * When started on the command line with a debug level >= 2, the 645d681e99Schristos * driver dumps the raw received data and the parser input to 655d681e99Schristos * stdout. Since the debug level is global, NTPD starts to create a 665d681e99Schristos * *lot* of output. It makes sense to pipe it through '(f)grep 675d681e99Schristos * GPSD_JSON' before writing the result to disk. 685d681e99Schristos * 695d681e99Schristos * A bit less intrusive is using netcat or telnet to connect to GPSD 705d681e99Schristos * and snoop what NTPD would get. If you try this, you have to send a 715d681e99Schristos * WATCH command to GPSD: 725d681e99Schristos * 735d681e99Schristos * ?WATCH={"device":"/dev/gps0","enable":true,"json":true,"pps":true};<CRLF> 745d681e99Schristos * 755d681e99Schristos * should show you what GPSD has to say to NTPD. Replace "/dev/gps0" 765d681e99Schristos * with the device link used by GPSD, if necessary. 77b8ecfcfeSchristos */ 78b8ecfcfeSchristos 795d681e99Schristos 80b8ecfcfeSchristos #ifdef HAVE_CONFIG_H 81b8ecfcfeSchristos #include <config.h> 82b8ecfcfeSchristos #endif 83b8ecfcfeSchristos 84b8ecfcfeSchristos #include "ntp_types.h" 85b8ecfcfeSchristos 86b8ecfcfeSchristos #if defined(REFCLOCK) && defined(CLOCK_GPSDJSON) && !defined(SYS_WINNT) 87b8ecfcfeSchristos 88b8ecfcfeSchristos /* ===================================================================== 895d681e99Schristos * Get the little JSMN library directly into our guts. Use the 'parent 905d681e99Schristos * link' feature for maximum speed. 91b8ecfcfeSchristos */ 925d681e99Schristos #define JSMN_PARENT_LINKS 93b8ecfcfeSchristos #include "../libjsmn/jsmn.c" 94b8ecfcfeSchristos 95b8ecfcfeSchristos /* ===================================================================== 965d681e99Schristos * JSON parsing stuff 975d681e99Schristos */ 985d681e99Schristos 995d681e99Schristos #define JSMN_MAXTOK 350 1005d681e99Schristos #define INVALID_TOKEN (-1) 1015d681e99Schristos 1025d681e99Schristos typedef struct json_ctx { 1035d681e99Schristos char * buf; 1045d681e99Schristos int ntok; 1055d681e99Schristos jsmntok_t tok[JSMN_MAXTOK]; 1065d681e99Schristos } json_ctx; 1075d681e99Schristos 1085d681e99Schristos typedef int tok_ref; 1095d681e99Schristos 1105d681e99Schristos /* Not all targets have 'long long', and not all of them have 'strtoll'. 1115d681e99Schristos * Sigh. We roll our own integer number parser. 1125d681e99Schristos */ 1135d681e99Schristos #ifdef HAVE_LONG_LONG 1145d681e99Schristos typedef signed long long int json_int; 1155d681e99Schristos typedef unsigned long long int json_uint; 1165d681e99Schristos #define JSON_INT_MAX LLONG_MAX 1175d681e99Schristos #define JSON_INT_MIN LLONG_MIN 1185d681e99Schristos #else 1195d681e99Schristos typedef signed long int json_int; 1205d681e99Schristos typedef unsigned long int json_uint; 1215d681e99Schristos #define JSON_INT_MAX LONG_MAX 1225d681e99Schristos #define JSON_INT_MIN LONG_MIN 1235d681e99Schristos #endif 1245d681e99Schristos 1255d681e99Schristos /* ===================================================================== 126b8ecfcfeSchristos * header stuff we need 127b8ecfcfeSchristos */ 128b8ecfcfeSchristos 129b8ecfcfeSchristos #include <netdb.h> 130b8ecfcfeSchristos #include <unistd.h> 131b8ecfcfeSchristos #include <fcntl.h> 132b8ecfcfeSchristos #include <string.h> 133b8ecfcfeSchristos #include <ctype.h> 1345d681e99Schristos #include <math.h> 135b8ecfcfeSchristos 136b8ecfcfeSchristos #include <sys/types.h> 137b8ecfcfeSchristos #include <sys/socket.h> 138b8ecfcfeSchristos #include <sys/stat.h> 139b8ecfcfeSchristos #include <netinet/tcp.h> 140b8ecfcfeSchristos 141b8ecfcfeSchristos #if defined(HAVE_SYS_POLL_H) 142b8ecfcfeSchristos # include <sys/poll.h> 1437476e6e4Schristos #elif defined(HAVE_SYS_SELECT_H) 144b8ecfcfeSchristos # include <sys/select.h> 145b8ecfcfeSchristos #else 146b8ecfcfeSchristos # error need poll() or select() 147b8ecfcfeSchristos #endif 148b8ecfcfeSchristos 149b8ecfcfeSchristos #include "ntpd.h" 150b8ecfcfeSchristos #include "ntp_io.h" 151b8ecfcfeSchristos #include "ntp_unixtime.h" 152b8ecfcfeSchristos #include "ntp_refclock.h" 153b8ecfcfeSchristos #include "ntp_stdlib.h" 154b8ecfcfeSchristos #include "ntp_calendar.h" 155*eabc0478Schristos #include "ntp_clockdev.h" 156b8ecfcfeSchristos #include "timespecops.h" 157b8ecfcfeSchristos 1585d681e99Schristos /* get operation modes from mode word. 1595d681e99Schristos 1605d681e99Schristos * + SERIAL (default) evaluates only serial time information ('STI') as 1615d681e99Schristos * provided by TPV and TOFF records. TPV evaluation suffers from a 1625d681e99Schristos * bigger jitter than TOFF, sine it does not contain the receive time 1635d681e99Schristos * from GPSD and therefore the receive time of NTPD must be 1645d681e99Schristos * substituted for it. The network latency makes this a second rate 1655d681e99Schristos * guess. 1665d681e99Schristos * 1675d681e99Schristos * If TOFF records are detected in the data stream, the timing 1685d681e99Schristos * information is gleaned from this record -- it contains the local 1695d681e99Schristos * receive time stamp from GPSD and therefore eliminates the 1705d681e99Schristos * transmission latency between GPSD and NTPD. The timing information 1715d681e99Schristos * from TPV is ignored once a TOFF is detected or expected. 1725d681e99Schristos * 1735d681e99Schristos * TPV is still used to check the fix status, so the driver can stop 1745d681e99Schristos * feeding samples when GPSD says that the time information is 1755d681e99Schristos * effectively unreliable. 1765d681e99Schristos * 1775d681e99Schristos * + STRICT means only feed clock samples when a valid STI/PPS pair is 1785d681e99Schristos * available. Combines the reference time from STI with the pulse time 1795d681e99Schristos * from PPS. Masks the serial data jitter as long PPS is available, 1805d681e99Schristos * but can rapidly deteriorate once PPS drops out. 1815d681e99Schristos * 1825d681e99Schristos * + AUTO tries to use STI/PPS pairs if available for some time, and if 1835d681e99Schristos * this fails for too long switches back to STI only until the PPS 1845d681e99Schristos * signal becomes available again. See the HTML docs for this driver 1855d681e99Schristos * about the gotchas and why this is not the default. 1865d681e99Schristos */ 1875d681e99Schristos #define MODE_OP_MASK 0x03 1885d681e99Schristos #define MODE_OP_STI 0 1895d681e99Schristos #define MODE_OP_STRICT 1 1905d681e99Schristos #define MODE_OP_AUTO 2 1915d681e99Schristos #define MODE_OP_MAXVAL 2 1925d681e99Schristos #define MODE_OP_MODE(x) ((x) & MODE_OP_MASK) 1935d681e99Schristos 194b8ecfcfeSchristos #define PRECISION (-9) /* precision assumed (about 2 ms) */ 195b8ecfcfeSchristos #define PPS_PRECISION (-20) /* precision assumed (about 1 us) */ 196b8ecfcfeSchristos #define REFID "GPSD" /* reference id */ 197b8ecfcfeSchristos #define DESCRIPTION "GPSD JSON client clock" /* who we are */ 198b8ecfcfeSchristos 199*eabc0478Schristos #define MAX_PDU_LEN 8192 /* multi-GNSS reports can be HUGE */ 200b8ecfcfeSchristos #define TICKOVER_LOW 10 201b8ecfcfeSchristos #define TICKOVER_HIGH 120 202b8ecfcfeSchristos #define LOGTHROTTLE 3600 203b8ecfcfeSchristos 2045d681e99Schristos /* Primary channel PPS avilability dance: 2055d681e99Schristos * Every good PPS sample gets us a credit of PPS_INCCOUNT points, every 2065d681e99Schristos * bad/missing PPS sample costs us a debit of PPS_DECCOUNT points. When 2075d681e99Schristos * the account reaches the upper limit we change to a mode where only 2085d681e99Schristos * PPS-augmented samples are fed to the core; when the account drops to 2095d681e99Schristos * zero we switch to a mode where TPV-only timestamps are fed to the 2105d681e99Schristos * core. 2115d681e99Schristos * This reduces the chance of rapid alternation between raw and 2125d681e99Schristos * PPS-augmented time stamps. 2135d681e99Schristos */ 2145d681e99Schristos #define PPS_MAXCOUNT 60 /* upper limit of account */ 2155d681e99Schristos #define PPS_INCCOUNT 3 /* credit for good samples */ 2165d681e99Schristos #define PPS_DECCOUNT 1 /* debit for bad samples */ 2175d681e99Schristos 2185d681e99Schristos /* The secondary (PPS) channel uses a different strategy to avoid old 2195d681e99Schristos * PPS samples in the median filter. 2205d681e99Schristos */ 2215d681e99Schristos #define PPS2_MAXCOUNT 10 222b8ecfcfeSchristos 223b8ecfcfeSchristos #ifndef BOOL 224b8ecfcfeSchristos # define BOOL int 225b8ecfcfeSchristos #endif 226b8ecfcfeSchristos #ifndef TRUE 227b8ecfcfeSchristos # define TRUE 1 228b8ecfcfeSchristos #endif 229b8ecfcfeSchristos #ifndef FALSE 230b8ecfcfeSchristos # define FALSE 0 231b8ecfcfeSchristos #endif 232b8ecfcfeSchristos 2335d681e99Schristos #define PROTO_VERSION(hi,lo) \ 2345d681e99Schristos ((((uint32_t)(hi) << 16) & 0xFFFF0000u) | \ 2355d681e99Schristos ((uint32_t)(lo) & 0x0FFFFu)) 2365d681e99Schristos 237b8ecfcfeSchristos /* some local typedefs: The NTPD formatting style cries for short type 238b8ecfcfeSchristos * names, and we provide them locally. Note:the suffix '_t' is reserved 239b8ecfcfeSchristos * for the standard; I use a capital T instead. 240b8ecfcfeSchristos */ 241b8ecfcfeSchristos typedef struct peer peerT; 242b8ecfcfeSchristos typedef struct refclockproc clockprocT; 243b8ecfcfeSchristos typedef struct addrinfo addrinfoT; 244b8ecfcfeSchristos 245b8ecfcfeSchristos /* ===================================================================== 246b8ecfcfeSchristos * We use the same device name scheme as does the NMEA driver; since 247b8ecfcfeSchristos * GPSD supports the same links, we can select devices by a fixed name. 248b8ecfcfeSchristos */ 249b8ecfcfeSchristos static const char * s_dev_stem = "/dev/gps"; 250b8ecfcfeSchristos 251b8ecfcfeSchristos /* ===================================================================== 252b8ecfcfeSchristos * forward declarations for transfer vector and the vector itself 253b8ecfcfeSchristos */ 254b8ecfcfeSchristos 255b8ecfcfeSchristos static void gpsd_init (void); 256b8ecfcfeSchristos static int gpsd_start (int, peerT *); 257b8ecfcfeSchristos static void gpsd_shutdown (int, peerT *); 258b8ecfcfeSchristos static void gpsd_receive (struct recvbuf *); 259b8ecfcfeSchristos static void gpsd_poll (int, peerT *); 260b8ecfcfeSchristos static void gpsd_control (int, const struct refclockstat *, 261b8ecfcfeSchristos struct refclockstat *, peerT *); 262b8ecfcfeSchristos static void gpsd_timer (int, peerT *); 263b8ecfcfeSchristos 2645d681e99Schristos static int myasprintf(char**, char const*, ...) NTP_PRINTF(2, 3); 2655d681e99Schristos 2665d681e99Schristos static void enter_opmode(peerT *peer, int mode); 2675d681e99Schristos static void leave_opmode(peerT *peer, int mode); 268b8ecfcfeSchristos 269b8ecfcfeSchristos struct refclock refclock_gpsdjson = { 270b8ecfcfeSchristos gpsd_start, /* start up driver */ 271b8ecfcfeSchristos gpsd_shutdown, /* shut down driver */ 272b8ecfcfeSchristos gpsd_poll, /* transmit poll message */ 273b8ecfcfeSchristos gpsd_control, /* fudge control */ 274b8ecfcfeSchristos gpsd_init, /* initialize driver */ 275b8ecfcfeSchristos noentry, /* buginfo */ 276b8ecfcfeSchristos gpsd_timer /* called once per second */ 277b8ecfcfeSchristos }; 278b8ecfcfeSchristos 279b8ecfcfeSchristos /* ===================================================================== 280b8ecfcfeSchristos * our local clock unit and data 281b8ecfcfeSchristos */ 2825d681e99Schristos struct gpsd_unit; 2835d681e99Schristos typedef struct gpsd_unit gpsd_unitT; 284b8ecfcfeSchristos 2855d681e99Schristos struct gpsd_unit { 2865d681e99Schristos /* links for sharing between master/slave units */ 2875d681e99Schristos gpsd_unitT *next_unit; 2885d681e99Schristos size_t refcount; 2895d681e99Schristos 2905d681e99Schristos /* data for the secondary PPS channel */ 2915d681e99Schristos peerT *pps_peer; 2925d681e99Schristos 2935d681e99Schristos /* unit and operation modes */ 2945d681e99Schristos int unit; 2955d681e99Schristos int mode; 2965d681e99Schristos char *logname; /* cached name for log/print */ 2975d681e99Schristos char * device; /* device name of unit */ 2985d681e99Schristos 2995d681e99Schristos /* current line protocol version */ 3005d681e99Schristos uint32_t proto_version; 3015d681e99Schristos 3025d681e99Schristos /* PPS time stamps primary + secondary channel */ 303b8ecfcfeSchristos l_fp pps_local; /* when we received the PPS message */ 304b8ecfcfeSchristos l_fp pps_stamp; /* related reference time */ 305b8ecfcfeSchristos l_fp pps_recvt; /* when GPSD detected the pulse */ 3065d681e99Schristos l_fp pps_stamp2;/* related reference time (secondary) */ 3075d681e99Schristos l_fp pps_recvt2;/* when GPSD detected the pulse (secondary)*/ 3085d681e99Schristos int ppscount; /* PPS counter (primary unit) */ 3095d681e99Schristos int ppscount2; /* PPS counter (secondary unit) */ 310b8ecfcfeSchristos 3115d681e99Schristos /* TPV or TOFF serial time information */ 3125d681e99Schristos l_fp sti_local; /* when we received the TPV/TOFF message */ 3135d681e99Schristos l_fp sti_stamp; /* effective GPS time stamp */ 3145d681e99Schristos l_fp sti_recvt; /* when GPSD got the fix */ 3155d681e99Schristos 3165d681e99Schristos /* precision estimates */ 3175d681e99Schristos int16_t sti_prec; /* serial precision based on EPT */ 3185d681e99Schristos int16_t pps_prec; /* PPS precision from GPSD or above */ 319b8ecfcfeSchristos 320b8ecfcfeSchristos /* fudge values for correction, mirrored as 'l_fp' */ 3215d681e99Schristos l_fp pps_fudge; /* PPS fudge primary channel */ 3225d681e99Schristos l_fp pps_fudge2; /* PPS fudge secondary channel */ 3235d681e99Schristos l_fp sti_fudge; /* TPV/TOFF serial data fudge */ 324b8ecfcfeSchristos 325b8ecfcfeSchristos /* Flags to indicate available data */ 3265d681e99Schristos int fl_nosync: 1; /* GPSD signals bad quality */ 3275d681e99Schristos int fl_sti : 1; /* valid TPV/TOFF seen (have time) */ 328b8ecfcfeSchristos int fl_pps : 1; /* valid pulse seen */ 3295d681e99Schristos int fl_pps2 : 1; /* valid pulse seen for PPS channel */ 3305d681e99Schristos int fl_rawsti: 1; /* permit raw TPV/TOFF time stamps */ 331b8ecfcfeSchristos int fl_vers : 1; /* have protocol version */ 332b8ecfcfeSchristos int fl_watch : 1; /* watch reply seen */ 3335d681e99Schristos /* protocol flags */ 3345d681e99Schristos int pf_nsec : 1; /* have nanosec PPS info */ 3355d681e99Schristos int pf_toff : 1; /* have TOFF record for timing */ 336b8ecfcfeSchristos 337b8ecfcfeSchristos /* admin stuff for sockets and device selection */ 338b8ecfcfeSchristos int fdt; /* current connecting socket */ 339b8ecfcfeSchristos addrinfoT * addr; /* next address to try */ 340b8ecfcfeSchristos u_int tickover; /* timeout countdown */ 341b8ecfcfeSchristos u_int tickpres; /* timeout preset */ 342b8ecfcfeSchristos 343b8ecfcfeSchristos /* tallies for the various events */ 344b8ecfcfeSchristos u_int tc_recv; /* received known records */ 3455d681e99Schristos u_int tc_breply; /* bad replies / parsing errors */ 3465d681e99Schristos u_int tc_nosync; /* TPV / sample cycles w/o fix */ 3475d681e99Schristos u_int tc_sti_recv;/* received serial time info records */ 3485d681e99Schristos u_int tc_sti_used;/* used --^-- */ 3495d681e99Schristos u_int tc_pps_recv;/* received PPS timing info records */ 3505d681e99Schristos u_int tc_pps_used;/* used --^-- */ 351b8ecfcfeSchristos 352b8ecfcfeSchristos /* log bloat throttle */ 353b8ecfcfeSchristos u_int logthrottle;/* seconds to next log slot */ 354b8ecfcfeSchristos 3555d681e99Schristos /* The parse context for the current record */ 3565d681e99Schristos json_ctx json_parse; 3575d681e99Schristos 358b8ecfcfeSchristos /* record assemby buffer and saved length */ 359b8ecfcfeSchristos int buflen; 360b8ecfcfeSchristos char buffer[MAX_PDU_LEN]; 3615d681e99Schristos }; 362b8ecfcfeSchristos 363b8ecfcfeSchristos /* ===================================================================== 364b8ecfcfeSchristos * static local helpers forward decls 365b8ecfcfeSchristos */ 366b8ecfcfeSchristos static void gpsd_init_socket(peerT * const peer); 367b8ecfcfeSchristos static void gpsd_test_socket(peerT * const peer); 368b8ecfcfeSchristos static void gpsd_stop_socket(peerT * const peer); 369b8ecfcfeSchristos 370b8ecfcfeSchristos static void gpsd_parse(peerT * const peer, 371b8ecfcfeSchristos const l_fp * const rtime); 372b8ecfcfeSchristos static BOOL convert_ascii_time(l_fp * fp, const char * gps_time); 373b8ecfcfeSchristos static void save_ltc(clockprocT * const pp, const char * const tc); 374b8ecfcfeSchristos static int syslogok(clockprocT * const pp, gpsd_unitT * const up); 375*eabc0478Schristos static void log_data(peerT *peer, int level, const char *what, 3765d681e99Schristos const char *buf, size_t len); 3775d681e99Schristos static int16_t clamped_precision(int rawprec); 378b8ecfcfeSchristos 379b8ecfcfeSchristos /* ===================================================================== 380b8ecfcfeSchristos * local / static stuff 381b8ecfcfeSchristos */ 382b8ecfcfeSchristos 3835d681e99Schristos static const char * const s_req_version = 3845d681e99Schristos "?VERSION;\r\n"; 3855d681e99Schristos 3865d681e99Schristos /* We keep a static list of network addresses for 'localhost:gpsd' or a 3875d681e99Schristos * fallback alias of it, and we try to connect to them in round-robin 3885d681e99Schristos * fashion. The service lookup is done during the driver init 3895d681e99Schristos * function to minmise the impact of 'getaddrinfo()'. 3905d681e99Schristos * 3915d681e99Schristos * Alas, the init function is called even if there are no clocks 3925d681e99Schristos * configured for this driver. So it makes sense to defer the logging of 3935d681e99Schristos * any errors or other notifications until the first clock unit is 3945d681e99Schristos * started -- otherwise there might be syslog entries from a driver that 3955d681e99Schristos * is not used at all. 396b8ecfcfeSchristos */ 397b8ecfcfeSchristos static addrinfoT *s_gpsd_addr; 3985d681e99Schristos static gpsd_unitT *s_clock_units; 3995d681e99Schristos 4005d681e99Schristos /* list of service/socket names we want to resolve against */ 4015d681e99Schristos static const char * const s_svctab[][2] = { 4025d681e99Schristos { "localhost", "gpsd" }, 4035d681e99Schristos { "localhost", "2947" }, 4045d681e99Schristos { "127.0.0.1", "2947" }, 4055d681e99Schristos { NULL, NULL } 4065d681e99Schristos }; 4075d681e99Schristos 4085d681e99Schristos /* list of address resolution errors and index of service entry that 4095d681e99Schristos * finally worked. 4105d681e99Schristos */ 4115d681e99Schristos static int s_svcerr[sizeof(s_svctab)/sizeof(s_svctab[0])]; 4125d681e99Schristos static int s_svcidx; 413b8ecfcfeSchristos 414b8ecfcfeSchristos /* ===================================================================== 415b8ecfcfeSchristos * log throttling 416b8ecfcfeSchristos */ 417b8ecfcfeSchristos static int/*BOOL*/ 418b8ecfcfeSchristos syslogok( 419b8ecfcfeSchristos clockprocT * const pp, 420b8ecfcfeSchristos gpsd_unitT * const up) 421b8ecfcfeSchristos { 422b8ecfcfeSchristos int res = (0 != (pp->sloppyclockflag & CLK_FLAG3)) 423b8ecfcfeSchristos || (0 == up->logthrottle ) 424b8ecfcfeSchristos || (LOGTHROTTLE == up->logthrottle ); 425b8ecfcfeSchristos if (res) 426b8ecfcfeSchristos up->logthrottle = LOGTHROTTLE; 427b8ecfcfeSchristos return res; 428b8ecfcfeSchristos } 429b8ecfcfeSchristos 430b8ecfcfeSchristos /* ===================================================================== 431b8ecfcfeSchristos * the clock functions 432b8ecfcfeSchristos */ 433b8ecfcfeSchristos 434b8ecfcfeSchristos /* --------------------------------------------------------------------- 435b8ecfcfeSchristos * Init: This currently just gets the socket address for the GPS daemon 436b8ecfcfeSchristos */ 437b8ecfcfeSchristos static void 438b8ecfcfeSchristos gpsd_init(void) 439b8ecfcfeSchristos { 440b8ecfcfeSchristos addrinfoT hints; 4415d681e99Schristos int rc, idx; 442b8ecfcfeSchristos 4435d681e99Schristos memset(s_svcerr, 0, sizeof(s_svcerr)); 444b8ecfcfeSchristos memset(&hints, 0, sizeof(hints)); 445b8ecfcfeSchristos hints.ai_family = AF_UNSPEC; 446b8ecfcfeSchristos hints.ai_protocol = IPPROTO_TCP; 447b8ecfcfeSchristos hints.ai_socktype = SOCK_STREAM; 448b8ecfcfeSchristos 4495d681e99Schristos for (idx = 0; s_svctab[idx][0] && !s_gpsd_addr; idx++) { 4505d681e99Schristos rc = getaddrinfo(s_svctab[idx][0], s_svctab[idx][1], 4515d681e99Schristos &hints, &s_gpsd_addr); 4525d681e99Schristos s_svcerr[idx] = rc; 4535d681e99Schristos if (0 == rc) 4545d681e99Schristos break; 455b8ecfcfeSchristos s_gpsd_addr = NULL; 456b8ecfcfeSchristos } 4575d681e99Schristos s_svcidx = idx; 4585d681e99Schristos } 4595d681e99Schristos 4605d681e99Schristos /* --------------------------------------------------------------------- 4615d681e99Schristos * Init Check: flush pending log messages and check if we can proceed 4625d681e99Schristos */ 4635d681e99Schristos static int/*BOOL*/ 4645d681e99Schristos gpsd_init_check(void) 4655d681e99Schristos { 4665d681e99Schristos int idx; 4675d681e99Schristos 4685d681e99Schristos /* Check if there is something to log */ 4695d681e99Schristos if (s_svcidx == 0) 4705d681e99Schristos return (s_gpsd_addr != NULL); 4715d681e99Schristos 4725d681e99Schristos /* spool out the resolver errors */ 4735d681e99Schristos for (idx = 0; idx < s_svcidx; ++idx) { 4745d681e99Schristos msyslog(LOG_WARNING, 4755d681e99Schristos "GPSD_JSON: failed to resolve '%s:%s', rc=%d (%s)", 4765d681e99Schristos s_svctab[idx][0], s_svctab[idx][1], 4775d681e99Schristos s_svcerr[idx], gai_strerror(s_svcerr[idx])); 4785d681e99Schristos } 4795d681e99Schristos 4805d681e99Schristos /* check if it was fatal, or if we can proceed */ 4815d681e99Schristos if (s_gpsd_addr == NULL) 4825d681e99Schristos msyslog(LOG_ERR, "%s", 4835d681e99Schristos "GPSD_JSON: failed to get socket address, giving up."); 4845d681e99Schristos else if (idx != 0) 4855d681e99Schristos msyslog(LOG_WARNING, 4865d681e99Schristos "GPSD_JSON: using '%s:%s' instead of '%s:%s'", 4875d681e99Schristos s_svctab[idx][0], s_svctab[idx][1], 4885d681e99Schristos s_svctab[0][0], s_svctab[0][1]); 4895d681e99Schristos 4905d681e99Schristos /* make sure this gets logged only once and tell if we can 4915d681e99Schristos * proceed or not 4925d681e99Schristos */ 4935d681e99Schristos s_svcidx = 0; 4945d681e99Schristos return (s_gpsd_addr != NULL); 4955d681e99Schristos } 496b8ecfcfeSchristos 497b8ecfcfeSchristos /* --------------------------------------------------------------------- 498b8ecfcfeSchristos * Start: allocate a unit pointer and set up the runtime data 499b8ecfcfeSchristos */ 500b8ecfcfeSchristos static int 501b8ecfcfeSchristos gpsd_start( 502b8ecfcfeSchristos int unit, 503b8ecfcfeSchristos peerT * peer) 504b8ecfcfeSchristos { 505b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 5065d681e99Schristos gpsd_unitT * up; 5075d681e99Schristos gpsd_unitT ** uscan = &s_clock_units; 508*eabc0478Schristos const char *tmpName; 509b8ecfcfeSchristos 510b8ecfcfeSchristos struct stat sb; 511*eabc0478Schristos char * devname = NULL; 512b8ecfcfeSchristos 5135d681e99Schristos /* check if we can proceed at all or if init failed */ 5145d681e99Schristos if ( ! gpsd_init_check()) 5155d681e99Schristos return FALSE; 5165d681e99Schristos 5175d681e99Schristos /* search for matching unit */ 5185d681e99Schristos while ((up = *uscan) != NULL && up->unit != (unit & 0x7F)) 5195d681e99Schristos uscan = &up->next_unit; 5205d681e99Schristos if (up == NULL) { 5215d681e99Schristos /* alloc unit, add to list and increment use count ASAP. */ 5225d681e99Schristos up = emalloc_zero(sizeof(*up)); 5235d681e99Schristos *uscan = up; 5245d681e99Schristos ++up->refcount; 5255d681e99Schristos 526b8ecfcfeSchristos /* initialize the unit structure */ 5275d681e99Schristos up->logname = estrdup(refnumtoa(&peer->srcadr)); 5285d681e99Schristos up->unit = unit & 0x7F; 529b8ecfcfeSchristos up->fdt = -1; 530b8ecfcfeSchristos up->addr = s_gpsd_addr; 531b8ecfcfeSchristos up->tickpres = TICKOVER_LOW; 532b8ecfcfeSchristos 5335d681e99Schristos /* Create the device name and check for a Character 5345d681e99Schristos * Device. It's assumed that GPSD was started with the 5355d681e99Schristos * same link, so the names match. (If this is not 5365d681e99Schristos * practicable, we will have to read the symlink, if 5375d681e99Schristos * any, so we can get the true device file.) 5385d681e99Schristos */ 539*eabc0478Schristos tmpName = clockdev_lookup(&peer->srcadr, 0); 540*eabc0478Schristos if (NULL != tmpName) { 541*eabc0478Schristos up->device = estrdup(tmpName); 542*eabc0478Schristos } else if (-1 == myasprintf(&up->device, "%s%u", s_dev_stem, up->unit)) { 5435d681e99Schristos msyslog(LOG_ERR, "%s: clock device name too long", 5445d681e99Schristos up->logname); 5455d681e99Schristos goto dev_fail; 5465d681e99Schristos } 547*eabc0478Schristos devname = up->device; 548*eabc0478Schristos up->device = ntp_realpath(devname); 549*eabc0478Schristos if (NULL == up->device) { 550*eabc0478Schristos msyslog(LOG_ERR, "%s: '%s' has no absolute path", 551*eabc0478Schristos up->logname, devname); 552*eabc0478Schristos goto dev_fail; 553*eabc0478Schristos } 554*eabc0478Schristos free(devname); 555*eabc0478Schristos devname = NULL; 556*eabc0478Schristos if (-1 == lstat(up->device, &sb)) { 557*eabc0478Schristos msyslog(LOG_ERR, "%s: '%s' not accessible", 558*eabc0478Schristos up->logname, up->device); 559*eabc0478Schristos goto dev_fail; 560*eabc0478Schristos } 561*eabc0478Schristos if (!S_ISCHR(sb.st_mode)) { 5625d681e99Schristos msyslog(LOG_ERR, "%s: '%s' is not a character device", 5635d681e99Schristos up->logname, up->device); 5645d681e99Schristos goto dev_fail; 5655d681e99Schristos } 5665d681e99Schristos } else { 5675d681e99Schristos /* All set up, just increment use count. */ 5685d681e99Schristos ++up->refcount; 5695d681e99Schristos } 5705d681e99Schristos 571b8ecfcfeSchristos /* setup refclock processing */ 572b8ecfcfeSchristos pp->unitptr = (caddr_t)up; 573b8ecfcfeSchristos pp->io.fd = -1; 574b8ecfcfeSchristos pp->io.clock_recv = gpsd_receive; 575b8ecfcfeSchristos pp->io.srcclock = peer; 576b8ecfcfeSchristos pp->io.datalen = 0; 577b8ecfcfeSchristos pp->a_lastcode[0] = '\0'; 578b8ecfcfeSchristos pp->lencode = 0; 579b8ecfcfeSchristos pp->clockdesc = DESCRIPTION; 580b8ecfcfeSchristos memcpy(&pp->refid, REFID, 4); 581b8ecfcfeSchristos 582b8ecfcfeSchristos /* Initialize miscellaneous variables */ 5835d681e99Schristos if (unit >= 128) 5845d681e99Schristos peer->precision = PPS_PRECISION; 5855d681e99Schristos else 586b8ecfcfeSchristos peer->precision = PRECISION; 587b8ecfcfeSchristos 5885d681e99Schristos /* If the daemon name lookup failed, just give up now. */ 5895d681e99Schristos if (NULL == up->addr) { 5905d681e99Schristos msyslog(LOG_ERR, "%s: no GPSD socket address, giving up", 5915d681e99Schristos up->logname); 592b8ecfcfeSchristos goto dev_fail; 593b8ecfcfeSchristos } 5945d681e99Schristos 595b8ecfcfeSchristos LOGIF(CLOCKINFO, 596b8ecfcfeSchristos (LOG_NOTICE, "%s: startup, device is '%s'", 597b8ecfcfeSchristos refnumtoa(&peer->srcadr), up->device)); 5985d681e99Schristos up->mode = MODE_OP_MODE(peer->ttl); 5995d681e99Schristos if (up->mode > MODE_OP_MAXVAL) 6005d681e99Schristos up->mode = 0; 6015d681e99Schristos if (unit >= 128) 6025d681e99Schristos up->pps_peer = peer; 6035d681e99Schristos else 6045d681e99Schristos enter_opmode(peer, up->mode); 605b8ecfcfeSchristos return TRUE; 606b8ecfcfeSchristos 607b8ecfcfeSchristos dev_fail: 608b8ecfcfeSchristos /* On failure, remove all UNIT ressources and declare defeat. */ 609*eabc0478Schristos free(devname); 610b8ecfcfeSchristos INSIST (up); 6115d681e99Schristos if (!--up->refcount) { 6125d681e99Schristos *uscan = up->next_unit; 613b8ecfcfeSchristos free(up->device); 614b8ecfcfeSchristos free(up); 6155d681e99Schristos } 616b8ecfcfeSchristos 617b8ecfcfeSchristos pp->unitptr = (caddr_t)NULL; 618b8ecfcfeSchristos return FALSE; 619b8ecfcfeSchristos } 620b8ecfcfeSchristos 621b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 622b8ecfcfeSchristos 623b8ecfcfeSchristos static void 624b8ecfcfeSchristos gpsd_shutdown( 625b8ecfcfeSchristos int unit, 626b8ecfcfeSchristos peerT * peer) 627b8ecfcfeSchristos { 628b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 629b8ecfcfeSchristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 6305d681e99Schristos gpsd_unitT ** uscan = &s_clock_units; 631b8ecfcfeSchristos 632b8ecfcfeSchristos UNUSED_ARG(unit); 633b8ecfcfeSchristos 6345d681e99Schristos /* The unit pointer might have been removed already. */ 6355d681e99Schristos if (up == NULL) 6365d681e99Schristos return; 6375d681e99Schristos 6385d681e99Schristos /* now check if we must close IO resources */ 6395d681e99Schristos if (peer != up->pps_peer) { 6405d681e99Schristos if (-1 != pp->io.fd) { 6415d681e99Schristos DPRINTF(1, ("%s: closing clock, fd=%d\n", 6425d681e99Schristos up->logname, pp->io.fd)); 6435d681e99Schristos io_closeclock(&pp->io); 6445d681e99Schristos pp->io.fd = -1; 6455d681e99Schristos } 6465d681e99Schristos if (up->fdt != -1) 6475d681e99Schristos close(up->fdt); 6485d681e99Schristos } 6495d681e99Schristos /* decrement use count and eventually remove this unit. */ 6505d681e99Schristos if (!--up->refcount) { 6515d681e99Schristos /* unlink this unit */ 6525d681e99Schristos while (*uscan != NULL) 6535d681e99Schristos if (*uscan == up) 6545d681e99Schristos *uscan = up->next_unit; 6555d681e99Schristos else 6565d681e99Schristos uscan = &(*uscan)->next_unit; 6575d681e99Schristos free(up->logname); 658b8ecfcfeSchristos free(up->device); 659b8ecfcfeSchristos free(up); 660b8ecfcfeSchristos } 661b8ecfcfeSchristos pp->unitptr = (caddr_t)NULL; 662b8ecfcfeSchristos LOGIF(CLOCKINFO, 663b8ecfcfeSchristos (LOG_NOTICE, "%s: shutdown", refnumtoa(&peer->srcadr))); 664b8ecfcfeSchristos } 665b8ecfcfeSchristos 666b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 667b8ecfcfeSchristos 668b8ecfcfeSchristos static void 669b8ecfcfeSchristos gpsd_receive( 670b8ecfcfeSchristos struct recvbuf * rbufp) 671b8ecfcfeSchristos { 672b8ecfcfeSchristos /* declare & init control structure ptrs */ 673b8ecfcfeSchristos peerT * const peer = rbufp->recv_peer; 674b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 675b8ecfcfeSchristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 676b8ecfcfeSchristos 677b8ecfcfeSchristos const char *psrc, *esrc; 678b8ecfcfeSchristos char *pdst, *edst, ch; 679b8ecfcfeSchristos 6805d681e99Schristos /* log the data stream, if this is enabled */ 681*eabc0478Schristos log_data(peer, 3, "recv", (const char*)rbufp->recv_buffer, 6825d681e99Schristos (size_t)rbufp->recv_length); 6835d681e99Schristos 6845d681e99Schristos 685b8ecfcfeSchristos /* Since we're getting a raw stream data, we must assemble lines 686b8ecfcfeSchristos * in our receive buffer. We can't use neither 'refclock_gtraw' 687b8ecfcfeSchristos * not 'refclock_gtlin' here... We process chars until we reach 688b8ecfcfeSchristos * an EoL (that is, line feed) but we truncate the message if it 689b8ecfcfeSchristos * does not fit the buffer. GPSD might truncate messages, too, 690b8ecfcfeSchristos * so dealing with truncated buffers is necessary anyway. 691b8ecfcfeSchristos */ 692b8ecfcfeSchristos psrc = (const char*)rbufp->recv_buffer; 693b8ecfcfeSchristos esrc = psrc + rbufp->recv_length; 694b8ecfcfeSchristos 695b8ecfcfeSchristos pdst = up->buffer + up->buflen; 696*eabc0478Schristos edst = up->buffer + sizeof(up->buffer) - 1; /* for trailing NUL */ 697b8ecfcfeSchristos 698b8ecfcfeSchristos while (psrc != esrc) { 699b8ecfcfeSchristos ch = *psrc++; 700b8ecfcfeSchristos if (ch == '\n') { 701b8ecfcfeSchristos /* trim trailing whitespace & terminate buffer */ 702b8ecfcfeSchristos while (pdst != up->buffer && pdst[-1] <= ' ') 703b8ecfcfeSchristos --pdst; 704b8ecfcfeSchristos *pdst = '\0'; 705b8ecfcfeSchristos /* process data and reset buffer */ 7065d681e99Schristos up->buflen = pdst - up->buffer; 707b8ecfcfeSchristos gpsd_parse(peer, &rbufp->recv_time); 708b8ecfcfeSchristos pdst = up->buffer; 709b8ecfcfeSchristos } else if (pdst != edst) { 710b8ecfcfeSchristos /* add next char, ignoring leading whitespace */ 711b8ecfcfeSchristos if (ch > ' ' || pdst != up->buffer) 712b8ecfcfeSchristos *pdst++ = ch; 713b8ecfcfeSchristos } 714b8ecfcfeSchristos } 715b8ecfcfeSchristos up->buflen = pdst - up->buffer; 716b8ecfcfeSchristos up->tickover = TICKOVER_LOW; 717b8ecfcfeSchristos } 718b8ecfcfeSchristos 719b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 720b8ecfcfeSchristos 721b8ecfcfeSchristos static void 7225d681e99Schristos poll_primary( 7235d681e99Schristos peerT * const peer , 7245d681e99Schristos clockprocT * const pp , 7255d681e99Schristos gpsd_unitT * const up ) 7265d681e99Schristos { 7275d681e99Schristos if (pp->coderecv != pp->codeproc) { 7285d681e99Schristos /* all is well */ 7295d681e99Schristos pp->lastref = pp->lastrec; 7305d681e99Schristos refclock_report(peer, CEVNT_NOMINAL); 7315d681e99Schristos refclock_receive(peer); 7325d681e99Schristos } else { 7335d681e99Schristos /* Not working properly, admit to it. If we have no 7345d681e99Schristos * connection to GPSD, declare the clock as faulty. If 7355d681e99Schristos * there were bad replies, this is handled as the major 7365d681e99Schristos * cause, and everything else is just a timeout. 7375d681e99Schristos */ 7385d681e99Schristos peer->precision = PRECISION; 7395d681e99Schristos if (-1 == pp->io.fd) 7405d681e99Schristos refclock_report(peer, CEVNT_FAULT); 7415d681e99Schristos else if (0 != up->tc_breply) 7425d681e99Schristos refclock_report(peer, CEVNT_BADREPLY); 7435d681e99Schristos else 7445d681e99Schristos refclock_report(peer, CEVNT_TIMEOUT); 7455d681e99Schristos } 7465d681e99Schristos 7475d681e99Schristos if (pp->sloppyclockflag & CLK_FLAG4) 7485d681e99Schristos mprintf_clock_stats( 7495d681e99Schristos &peer->srcadr,"%u %u %u %u %u %u %u", 7505d681e99Schristos up->tc_recv, 7515d681e99Schristos up->tc_breply, up->tc_nosync, 7525d681e99Schristos up->tc_sti_recv, up->tc_sti_used, 7535d681e99Schristos up->tc_pps_recv, up->tc_pps_used); 7545d681e99Schristos 7555d681e99Schristos /* clear tallies for next round */ 7565d681e99Schristos up->tc_breply = 0; 7575d681e99Schristos up->tc_recv = 0; 7585d681e99Schristos up->tc_nosync = 0; 7595d681e99Schristos up->tc_sti_recv = 0; 7605d681e99Schristos up->tc_sti_used = 0; 7615d681e99Schristos up->tc_pps_recv = 0; 7625d681e99Schristos up->tc_pps_used = 0; 7635d681e99Schristos } 7645d681e99Schristos 7655d681e99Schristos static void 7665d681e99Schristos poll_secondary( 7675d681e99Schristos peerT * const peer , 7685d681e99Schristos clockprocT * const pp , 7695d681e99Schristos gpsd_unitT * const up ) 7705d681e99Schristos { 7715d681e99Schristos if (pp->coderecv != pp->codeproc) { 7725d681e99Schristos /* all is well */ 7735d681e99Schristos pp->lastref = pp->lastrec; 7745d681e99Schristos refclock_report(peer, CEVNT_NOMINAL); 7755d681e99Schristos refclock_receive(peer); 7765d681e99Schristos } else { 7775d681e99Schristos peer->precision = PPS_PRECISION; 7785d681e99Schristos peer->flags &= ~FLAG_PPS; 7795d681e99Schristos refclock_report(peer, CEVNT_TIMEOUT); 7805d681e99Schristos } 7815d681e99Schristos } 7825d681e99Schristos 7835d681e99Schristos static void 784b8ecfcfeSchristos gpsd_poll( 785b8ecfcfeSchristos int unit, 786b8ecfcfeSchristos peerT * peer) 787b8ecfcfeSchristos { 788b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 789b8ecfcfeSchristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 790b8ecfcfeSchristos 791b8ecfcfeSchristos ++pp->polls; 7925d681e99Schristos if (peer == up->pps_peer) 7935d681e99Schristos poll_secondary(peer, pp, up); 7945d681e99Schristos else 7955d681e99Schristos poll_primary(peer, pp, up); 796b8ecfcfeSchristos } 797b8ecfcfeSchristos 798b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 799b8ecfcfeSchristos 800b8ecfcfeSchristos static void 801b8ecfcfeSchristos gpsd_control( 802b8ecfcfeSchristos int unit, 803b8ecfcfeSchristos const struct refclockstat * in_st, 804b8ecfcfeSchristos struct refclockstat * out_st, 805b8ecfcfeSchristos peerT * peer ) 806b8ecfcfeSchristos { 807b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 808b8ecfcfeSchristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 809b8ecfcfeSchristos 8105d681e99Schristos if (peer == up->pps_peer) { 8115d681e99Schristos DTOLFP(pp->fudgetime1, &up->pps_fudge2); 8125d681e99Schristos if ( ! (pp->sloppyclockflag & CLK_FLAG1)) 8135d681e99Schristos peer->flags &= ~FLAG_PPS; 8145d681e99Schristos } else { 815b8ecfcfeSchristos /* save preprocessed fudge times */ 816b8ecfcfeSchristos DTOLFP(pp->fudgetime1, &up->pps_fudge); 8175d681e99Schristos DTOLFP(pp->fudgetime2, &up->sti_fudge); 8185d681e99Schristos 8195d681e99Schristos if (MODE_OP_MODE(up->mode ^ peer->ttl)) { 8205d681e99Schristos leave_opmode(peer, up->mode); 8215d681e99Schristos up->mode = MODE_OP_MODE(peer->ttl); 8225d681e99Schristos enter_opmode(peer, up->mode); 8235d681e99Schristos } 8245d681e99Schristos } 825b8ecfcfeSchristos } 826b8ecfcfeSchristos 827b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 828b8ecfcfeSchristos 829b8ecfcfeSchristos static void 8305d681e99Schristos timer_primary( 8315d681e99Schristos peerT * const peer , 8325d681e99Schristos clockprocT * const pp , 8335d681e99Schristos gpsd_unitT * const up ) 834b8ecfcfeSchristos { 835b8ecfcfeSchristos int rc; 836b8ecfcfeSchristos 837b8ecfcfeSchristos /* This is used for timeout handling. Nothing that needs 838b8ecfcfeSchristos * sub-second precison happens here, so receive/connect/retry 839b8ecfcfeSchristos * timeouts are simply handled by a count down, and then we 840b8ecfcfeSchristos * decide what to do by the socket values. 841b8ecfcfeSchristos * 842b8ecfcfeSchristos * Note that the timer stays at zero here, unless some of the 843b8ecfcfeSchristos * functions set it to another value. 844b8ecfcfeSchristos */ 845b8ecfcfeSchristos if (up->logthrottle) 846b8ecfcfeSchristos --up->logthrottle; 847b8ecfcfeSchristos if (up->tickover) 848b8ecfcfeSchristos --up->tickover; 849b8ecfcfeSchristos switch (up->tickover) { 850b8ecfcfeSchristos case 4: 8515d681e99Schristos /* If we are connected to GPSD, try to get a live signal 8525d681e99Schristos * by querying the version. Otherwise just check the 8535d681e99Schristos * socket to become ready. 854b8ecfcfeSchristos */ 855b8ecfcfeSchristos if (-1 != pp->io.fd) { 8565d681e99Schristos size_t rlen = strlen(s_req_version); 8575d681e99Schristos DPRINTF(2, ("%s: timer livecheck: '%s'\n", 8585d681e99Schristos up->logname, s_req_version)); 859*eabc0478Schristos log_data(peer, 2, "send", s_req_version, rlen); 8605d681e99Schristos rc = write(pp->io.fd, s_req_version, rlen); 861b8ecfcfeSchristos (void)rc; 862b8ecfcfeSchristos } else if (-1 != up->fdt) { 863b8ecfcfeSchristos gpsd_test_socket(peer); 864b8ecfcfeSchristos } 865b8ecfcfeSchristos break; 866b8ecfcfeSchristos 867b8ecfcfeSchristos case 0: 868b8ecfcfeSchristos if (-1 != pp->io.fd) 869b8ecfcfeSchristos gpsd_stop_socket(peer); 870b8ecfcfeSchristos else if (-1 != up->fdt) 871b8ecfcfeSchristos gpsd_test_socket(peer); 872b8ecfcfeSchristos else if (NULL != s_gpsd_addr) 873b8ecfcfeSchristos gpsd_init_socket(peer); 874b8ecfcfeSchristos break; 875b8ecfcfeSchristos 876b8ecfcfeSchristos default: 877b8ecfcfeSchristos if (-1 == pp->io.fd && -1 != up->fdt) 878b8ecfcfeSchristos gpsd_test_socket(peer); 879b8ecfcfeSchristos } 8805d681e99Schristos } 881b8ecfcfeSchristos 8825d681e99Schristos static void 8835d681e99Schristos timer_secondary( 8845d681e99Schristos peerT * const peer , 8855d681e99Schristos clockprocT * const pp , 8865d681e99Schristos gpsd_unitT * const up ) 8875d681e99Schristos { 8885d681e99Schristos /* Reduce the count by one. Flush sample buffer and clear PPS 8895d681e99Schristos * flag when this happens. 8905d681e99Schristos */ 8915d681e99Schristos up->ppscount2 = max(0, (up->ppscount2 - 1)); 8925d681e99Schristos if (0 == up->ppscount2) { 8935d681e99Schristos if (pp->coderecv != pp->codeproc) { 8945d681e99Schristos refclock_report(peer, CEVNT_TIMEOUT); 8955d681e99Schristos pp->coderecv = pp->codeproc; 8965d681e99Schristos } 897b8ecfcfeSchristos peer->flags &= ~FLAG_PPS; 898b8ecfcfeSchristos } 8995d681e99Schristos } 9005d681e99Schristos 9015d681e99Schristos static void 9025d681e99Schristos gpsd_timer( 9035d681e99Schristos int unit, 9045d681e99Schristos peerT * peer) 9055d681e99Schristos { 9065d681e99Schristos clockprocT * const pp = peer->procptr; 9075d681e99Schristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 9085d681e99Schristos 9095d681e99Schristos if (peer == up->pps_peer) 9105d681e99Schristos timer_secondary(peer, pp, up); 9115d681e99Schristos else 9125d681e99Schristos timer_primary(peer, pp, up); 9135d681e99Schristos } 9145d681e99Schristos 9155d681e99Schristos /* ===================================================================== 9165d681e99Schristos * handle opmode switches 9175d681e99Schristos */ 9185d681e99Schristos 9195d681e99Schristos static void 9205d681e99Schristos enter_opmode( 9215d681e99Schristos peerT *peer, 9225d681e99Schristos int mode) 9235d681e99Schristos { 9245d681e99Schristos clockprocT * const pp = peer->procptr; 9255d681e99Schristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 9265d681e99Schristos 9275d681e99Schristos DPRINTF(1, ("%s: enter operation mode %d\n", 9285d681e99Schristos up->logname, MODE_OP_MODE(mode))); 9295d681e99Schristos 9305d681e99Schristos if (MODE_OP_MODE(mode) == MODE_OP_AUTO) { 9315d681e99Schristos up->fl_rawsti = 0; 9325d681e99Schristos up->ppscount = PPS_MAXCOUNT / 2; 9335d681e99Schristos } 9345d681e99Schristos up->fl_pps = 0; 9355d681e99Schristos up->fl_sti = 0; 9365d681e99Schristos } 9375d681e99Schristos 9385d681e99Schristos /* ------------------------------------------------------------------ */ 9395d681e99Schristos 9405d681e99Schristos static void 9415d681e99Schristos leave_opmode( 9425d681e99Schristos peerT *peer, 9435d681e99Schristos int mode) 9445d681e99Schristos { 9455d681e99Schristos clockprocT * const pp = peer->procptr; 9465d681e99Schristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 9475d681e99Schristos 9485d681e99Schristos DPRINTF(1, ("%s: leaving operation mode %d\n", 9495d681e99Schristos up->logname, MODE_OP_MODE(mode))); 9505d681e99Schristos 9515d681e99Schristos if (MODE_OP_MODE(mode) == MODE_OP_AUTO) { 9525d681e99Schristos up->fl_rawsti = 0; 9535d681e99Schristos up->ppscount = 0; 9545d681e99Schristos } 9555d681e99Schristos up->fl_pps = 0; 9565d681e99Schristos up->fl_sti = 0; 9575d681e99Schristos } 9585d681e99Schristos 9595d681e99Schristos /* ===================================================================== 9605d681e99Schristos * operation mode specific evaluation 9615d681e99Schristos */ 9625d681e99Schristos 9635d681e99Schristos static void 9645d681e99Schristos add_clock_sample( 9655d681e99Schristos peerT * const peer , 9665d681e99Schristos clockprocT * const pp , 9675d681e99Schristos l_fp stamp, 9685d681e99Schristos l_fp recvt) 9695d681e99Schristos { 9705d681e99Schristos pp->lastref = stamp; 9715d681e99Schristos if (pp->coderecv == pp->codeproc) 9725d681e99Schristos refclock_report(peer, CEVNT_NOMINAL); 973cdfa2a7eSchristos refclock_process_offset(pp, stamp, recvt, 0.0); 9745d681e99Schristos } 9755d681e99Schristos 9765d681e99Schristos /* ------------------------------------------------------------------ */ 9775d681e99Schristos 9785d681e99Schristos static void 9795d681e99Schristos eval_strict( 9805d681e99Schristos peerT * const peer , 9815d681e99Schristos clockprocT * const pp , 9825d681e99Schristos gpsd_unitT * const up ) 9835d681e99Schristos { 9845d681e99Schristos if (up->fl_sti && up->fl_pps) { 9855d681e99Schristos /* use TPV reference time + PPS receive time */ 9865d681e99Schristos add_clock_sample(peer, pp, up->sti_stamp, up->pps_recvt); 9875d681e99Schristos peer->precision = up->pps_prec; 9885d681e99Schristos /* both packets consumed now... */ 9895d681e99Schristos up->fl_pps = 0; 9905d681e99Schristos up->fl_sti = 0; 9915d681e99Schristos ++up->tc_sti_used; 9925d681e99Schristos } 9935d681e99Schristos } 9945d681e99Schristos 9955d681e99Schristos /* ------------------------------------------------------------------ */ 9965d681e99Schristos /* PPS processing for the secondary channel. GPSD provides us with full 9975d681e99Schristos * timing information, so there's no danger of PLL-locking to the wrong 9985d681e99Schristos * second. The belts and suspenders needed for the raw ATOM clock are 9995d681e99Schristos * unnecessary here. 10005d681e99Schristos */ 10015d681e99Schristos static void 10025d681e99Schristos eval_pps_secondary( 10035d681e99Schristos peerT * const peer , 10045d681e99Schristos clockprocT * const pp , 10055d681e99Schristos gpsd_unitT * const up ) 10065d681e99Schristos { 10075d681e99Schristos if (up->fl_pps2) { 10085d681e99Schristos /* feed data */ 10095d681e99Schristos add_clock_sample(peer, pp, up->pps_stamp2, up->pps_recvt2); 10105d681e99Schristos peer->precision = up->pps_prec; 10115d681e99Schristos /* PPS peer flag logic */ 10125d681e99Schristos up->ppscount2 = min(PPS2_MAXCOUNT, (up->ppscount2 + 2)); 10135d681e99Schristos if ((PPS2_MAXCOUNT == up->ppscount2) && 10145d681e99Schristos (pp->sloppyclockflag & CLK_FLAG1) ) 10155d681e99Schristos peer->flags |= FLAG_PPS; 10165d681e99Schristos /* mark time stamp as burned... */ 10175d681e99Schristos up->fl_pps2 = 0; 10185d681e99Schristos ++up->tc_pps_used; 10195d681e99Schristos } 10205d681e99Schristos } 10215d681e99Schristos 10225d681e99Schristos /* ------------------------------------------------------------------ */ 10235d681e99Schristos 10245d681e99Schristos static void 10255d681e99Schristos eval_serial( 10265d681e99Schristos peerT * const peer , 10275d681e99Schristos clockprocT * const pp , 10285d681e99Schristos gpsd_unitT * const up ) 10295d681e99Schristos { 10305d681e99Schristos if (up->fl_sti) { 10315d681e99Schristos add_clock_sample(peer, pp, up->sti_stamp, up->sti_recvt); 10325d681e99Schristos peer->precision = up->sti_prec; 10335d681e99Schristos /* mark time stamp as burned... */ 10345d681e99Schristos up->fl_sti = 0; 10355d681e99Schristos ++up->tc_sti_used; 10365d681e99Schristos } 10375d681e99Schristos } 10385d681e99Schristos 10395d681e99Schristos /* ------------------------------------------------------------------ */ 10405d681e99Schristos static void 10415d681e99Schristos eval_auto( 10425d681e99Schristos peerT * const peer , 10435d681e99Schristos clockprocT * const pp , 10445d681e99Schristos gpsd_unitT * const up ) 10455d681e99Schristos { 10465d681e99Schristos /* If there's no TPV available, stop working here... */ 10475d681e99Schristos if (!up->fl_sti) 10485d681e99Schristos return; 10495d681e99Schristos 10505d681e99Schristos /* check how to handle STI+PPS: Can PPS be used to augment STI 10515d681e99Schristos * (or vice versae), do we drop the sample because there is a 10525d681e99Schristos * temporary missing PPS signal, or do we feed on STI time 10535d681e99Schristos * stamps alone? 10545d681e99Schristos * 10555d681e99Schristos * Do a counter/threshold dance to decide how to proceed. 10565d681e99Schristos */ 10575d681e99Schristos if (up->fl_pps) { 10585d681e99Schristos up->ppscount = min(PPS_MAXCOUNT, 10595d681e99Schristos (up->ppscount + PPS_INCCOUNT)); 10605d681e99Schristos if ((PPS_MAXCOUNT == up->ppscount) && up->fl_rawsti) { 10615d681e99Schristos up->fl_rawsti = 0; 10625d681e99Schristos msyslog(LOG_INFO, 10635d681e99Schristos "%s: expect valid PPS from now", 10645d681e99Schristos up->logname); 10655d681e99Schristos } 10665d681e99Schristos } else { 10675d681e99Schristos up->ppscount = max(0, (up->ppscount - PPS_DECCOUNT)); 10685d681e99Schristos if ((0 == up->ppscount) && !up->fl_rawsti) { 10695d681e99Schristos up->fl_rawsti = -1; 10705d681e99Schristos msyslog(LOG_WARNING, 10715d681e99Schristos "%s: use TPV alone from now", 10725d681e99Schristos up->logname); 10735d681e99Schristos } 10745d681e99Schristos } 10755d681e99Schristos 10765d681e99Schristos /* now eventually feed the sample */ 10775d681e99Schristos if (up->fl_rawsti) 10785d681e99Schristos eval_serial(peer, pp, up); 10795d681e99Schristos else 10805d681e99Schristos eval_strict(peer, pp, up); 10815d681e99Schristos } 1082b8ecfcfeSchristos 1083b8ecfcfeSchristos /* ===================================================================== 1084b8ecfcfeSchristos * JSON parsing stuff 1085b8ecfcfeSchristos */ 1086b8ecfcfeSchristos 10875d681e99Schristos /* ------------------------------------------------------------------ */ 10885d681e99Schristos /* Parse a decimal integer with a possible sign. Works like 'strtoll()' 10895d681e99Schristos * or 'strtol()', but with a fixed base of 10 and without eating away 10905d681e99Schristos * leading whitespace. For the error codes, the handling of the end 10915d681e99Schristos * pointer and the return values see 'strtol()'. 10925d681e99Schristos */ 10935d681e99Schristos static json_int 10945d681e99Schristos strtojint( 10955d681e99Schristos const char *cp, char **ep) 10965d681e99Schristos { 10975d681e99Schristos json_uint accu, limit_lo, limit_hi; 10985d681e99Schristos int flags; /* bit 0: overflow; bit 1: sign */ 10995d681e99Schristos const char * hold; 1100b8ecfcfeSchristos 11015d681e99Schristos /* pointer union to circumvent a tricky/sticky const issue */ 11025d681e99Schristos union { const char * c; char * v; } vep; 1103b8ecfcfeSchristos 11045d681e99Schristos /* store initial value of 'cp' -- see 'strtol()' */ 11055d681e99Schristos vep.c = cp; 1106b8ecfcfeSchristos 11075d681e99Schristos /* Eat away an optional sign and set the limits accordingly: The 11085d681e99Schristos * high limit is the maximum absolute value that can be returned, 11095d681e99Schristos * and the low limit is the biggest value that does not cause an 11105d681e99Schristos * overflow when multiplied with 10. Avoid negation overflows. 11115d681e99Schristos */ 11125d681e99Schristos if (*cp == '-') { 11135d681e99Schristos cp += 1; 11145d681e99Schristos flags = 2; 11155d681e99Schristos limit_hi = (json_uint)-(JSON_INT_MIN + 1) + 1; 11165d681e99Schristos } else { 11175d681e99Schristos cp += (*cp == '+'); 11185d681e99Schristos flags = 0; 11195d681e99Schristos limit_hi = (json_uint)JSON_INT_MAX; 11205d681e99Schristos } 11215d681e99Schristos limit_lo = limit_hi / 10; 11225d681e99Schristos 11235d681e99Schristos /* Now try to convert a sequence of digits. */ 11245d681e99Schristos hold = cp; 11255d681e99Schristos accu = 0; 1126af12ab5eSchristos while (isdigit(*(const u_char*)cp)) { 11275d681e99Schristos flags |= (accu > limit_lo); 1128af12ab5eSchristos accu = accu * 10 + (*(const u_char*)cp++ - '0'); 11295d681e99Schristos flags |= (accu > limit_hi); 11305d681e99Schristos } 11315d681e99Schristos /* Check for empty conversion (no digits seen). */ 11325d681e99Schristos if (hold != cp) 11335d681e99Schristos vep.c = cp; 11345d681e99Schristos else 11355d681e99Schristos errno = EINVAL; /* accu is still zero */ 11365d681e99Schristos /* Check for range overflow */ 11375d681e99Schristos if (flags & 1) { 11385d681e99Schristos errno = ERANGE; 11395d681e99Schristos accu = limit_hi; 11405d681e99Schristos } 11415d681e99Schristos /* If possible, store back the end-of-conversion pointer */ 11425d681e99Schristos if (ep) 11435d681e99Schristos *ep = vep.v; 11445d681e99Schristos /* If negative, return the negated result if the accu is not 11455d681e99Schristos * zero. Avoid negation overflows. 11465d681e99Schristos */ 11475d681e99Schristos if ((flags & 2) && accu) 11485d681e99Schristos return -(json_int)(accu - 1) - 1; 11495d681e99Schristos else 11505d681e99Schristos return (json_int)accu; 11515d681e99Schristos } 1152b8ecfcfeSchristos 1153b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1154b8ecfcfeSchristos 1155b8ecfcfeSchristos static tok_ref 1156b8ecfcfeSchristos json_token_skip( 1157b8ecfcfeSchristos const json_ctx * ctx, 1158b8ecfcfeSchristos tok_ref tid) 1159b8ecfcfeSchristos { 11605d681e99Schristos if (tid >= 0 && tid < ctx->ntok) { 11615d681e99Schristos int len = ctx->tok[tid].size; 11625d681e99Schristos /* For arrays and objects, the size is the number of 11635d681e99Schristos * ITEMS in the compound. Thats the number of objects in 11645d681e99Schristos * the array, and the number of key/value pairs for 11655d681e99Schristos * objects. In theory, the key must be a string, and we 11665d681e99Schristos * could simply skip one token before skipping the 11675d681e99Schristos * value, which can be anything. We're a bit paranoid 11685d681e99Schristos * and lazy at the same time: We simply double the 11695d681e99Schristos * number of tokens to skip and fall through into the 11705d681e99Schristos * array processing when encountering an object. 11715d681e99Schristos */ 11725d681e99Schristos switch (ctx->tok[tid].type) { 11735d681e99Schristos case JSMN_OBJECT: 11745d681e99Schristos len *= 2; 11755d681e99Schristos /* FALLTHROUGH */ 11765d681e99Schristos case JSMN_ARRAY: 1177b8ecfcfeSchristos for (++tid; len; --len) 1178b8ecfcfeSchristos tid = json_token_skip(ctx, tid); 1179b8ecfcfeSchristos break; 11805d681e99Schristos 11815d681e99Schristos default: 11825d681e99Schristos ++tid; 11835d681e99Schristos break; 11845d681e99Schristos } 118568dbbb44Schristos /* The next condition should never be true, but paranoia 118668dbbb44Schristos * prevails... 118768dbbb44Schristos */ 118868dbbb44Schristos if (tid < 0 || tid > ctx->ntok) 1189b8ecfcfeSchristos tid = ctx->ntok; 11905d681e99Schristos } 1191b8ecfcfeSchristos return tid; 1192b8ecfcfeSchristos } 1193b8ecfcfeSchristos 1194b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1195b8ecfcfeSchristos 1196b8ecfcfeSchristos static int 1197b8ecfcfeSchristos json_object_lookup( 1198b8ecfcfeSchristos const json_ctx * ctx , 1199b8ecfcfeSchristos tok_ref tid , 12005d681e99Schristos const char * key , 12015d681e99Schristos int what) 1202b8ecfcfeSchristos { 1203b8ecfcfeSchristos int len; 1204b8ecfcfeSchristos 12055d681e99Schristos if (tid < 0 || tid >= ctx->ntok || 12065d681e99Schristos ctx->tok[tid].type != JSMN_OBJECT) 1207b8ecfcfeSchristos return INVALID_TOKEN; 12085d681e99Schristos 1209b8ecfcfeSchristos len = ctx->tok[tid].size; 12105d681e99Schristos for (++tid; len && tid+1 < ctx->ntok; --len) { 12115d681e99Schristos if (ctx->tok[tid].type != JSMN_STRING) { /* Blooper! */ 12125d681e99Schristos tid = json_token_skip(ctx, tid); /* skip key */ 12135d681e99Schristos tid = json_token_skip(ctx, tid); /* skip val */ 12145d681e99Schristos } else if (strcmp(key, ctx->buf + ctx->tok[tid].start)) { 12155d681e99Schristos tid = json_token_skip(ctx, tid+1); /* skip key+val */ 121668dbbb44Schristos } else if (what < 0 || (u_int)what == ctx->tok[tid+1].type) { 1217b8ecfcfeSchristos return tid + 1; 12185d681e99Schristos } else { 12195d681e99Schristos break; 12205d681e99Schristos } 12215d681e99Schristos /* if skipping ahead returned an error, bail out here. */ 12225d681e99Schristos if (tid < 0) 12235d681e99Schristos break; 1224b8ecfcfeSchristos } 1225b8ecfcfeSchristos return INVALID_TOKEN; 1226b8ecfcfeSchristos } 1227b8ecfcfeSchristos 1228b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1229b8ecfcfeSchristos 12305d681e99Schristos static const char* 12315d681e99Schristos json_object_lookup_primitive( 12325d681e99Schristos const json_ctx * ctx, 12335d681e99Schristos tok_ref tid, 12345d681e99Schristos const char * key) 12355d681e99Schristos { 12365d681e99Schristos tid = json_object_lookup(ctx, tid, key, JSMN_PRIMITIVE); 12375d681e99Schristos if (INVALID_TOKEN != tid) 12385d681e99Schristos return ctx->buf + ctx->tok[tid].start; 12395d681e99Schristos else 12405d681e99Schristos return NULL; 12415d681e99Schristos } 12425d681e99Schristos /* ------------------------------------------------------------------ */ 12435d681e99Schristos /* look up a boolean value. This essentially returns a tribool: 12445d681e99Schristos * 0->false, 1->true, (-1)->error/undefined 12455d681e99Schristos */ 12465d681e99Schristos static int 12475d681e99Schristos json_object_lookup_bool( 12485d681e99Schristos const json_ctx * ctx, 12495d681e99Schristos tok_ref tid, 12505d681e99Schristos const char * key) 12515d681e99Schristos { 12525d681e99Schristos const char *cp; 12535d681e99Schristos cp = json_object_lookup_primitive(ctx, tid, key); 12545d681e99Schristos switch ( cp ? *cp : '\0') { 12555d681e99Schristos case 't': return 1; 12565d681e99Schristos case 'f': return 0; 12575d681e99Schristos default : return -1; 12585d681e99Schristos } 12595d681e99Schristos } 12605d681e99Schristos 12615d681e99Schristos /* ------------------------------------------------------------------ */ 12625d681e99Schristos 1263b8ecfcfeSchristos static const char* 1264b8ecfcfeSchristos json_object_lookup_string( 1265b8ecfcfeSchristos const json_ctx * ctx, 1266b8ecfcfeSchristos tok_ref tid, 1267b8ecfcfeSchristos const char * key) 1268b8ecfcfeSchristos { 12695d681e99Schristos tid = json_object_lookup(ctx, tid, key, JSMN_STRING); 12705d681e99Schristos if (INVALID_TOKEN != tid) 12715d681e99Schristos return ctx->buf + ctx->tok[tid].start; 1272b8ecfcfeSchristos return NULL; 1273b8ecfcfeSchristos } 1274b8ecfcfeSchristos 1275b8ecfcfeSchristos static const char* 1276b8ecfcfeSchristos json_object_lookup_string_default( 1277b8ecfcfeSchristos const json_ctx * ctx, 1278b8ecfcfeSchristos tok_ref tid, 1279b8ecfcfeSchristos const char * key, 1280b8ecfcfeSchristos const char * def) 1281b8ecfcfeSchristos { 12825d681e99Schristos tid = json_object_lookup(ctx, tid, key, JSMN_STRING); 12835d681e99Schristos if (INVALID_TOKEN != tid) 12845d681e99Schristos return ctx->buf + ctx->tok[tid].start; 1285b8ecfcfeSchristos return def; 1286b8ecfcfeSchristos } 1287b8ecfcfeSchristos 1288b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1289b8ecfcfeSchristos 1290b8ecfcfeSchristos static json_int 1291b8ecfcfeSchristos json_object_lookup_int( 1292b8ecfcfeSchristos const json_ctx * ctx, 1293b8ecfcfeSchristos tok_ref tid, 1294b8ecfcfeSchristos const char * key) 1295b8ecfcfeSchristos { 1296b8ecfcfeSchristos json_int ret; 12975d681e99Schristos const char * cp; 1298b8ecfcfeSchristos char * ep; 1299b8ecfcfeSchristos 13005d681e99Schristos cp = json_object_lookup_primitive(ctx, tid, key); 13015d681e99Schristos if (NULL != cp) { 13025d681e99Schristos ret = strtojint(cp, &ep); 13035d681e99Schristos if (cp != ep && '\0' == *ep) 1304b8ecfcfeSchristos return ret; 13055d681e99Schristos } else { 1306b8ecfcfeSchristos errno = EINVAL; 13075d681e99Schristos } 1308b8ecfcfeSchristos return 0; 1309b8ecfcfeSchristos } 1310b8ecfcfeSchristos 1311b8ecfcfeSchristos static json_int 1312b8ecfcfeSchristos json_object_lookup_int_default( 1313b8ecfcfeSchristos const json_ctx * ctx, 1314b8ecfcfeSchristos tok_ref tid, 1315b8ecfcfeSchristos const char * key, 1316b8ecfcfeSchristos json_int def) 1317b8ecfcfeSchristos { 13185d681e99Schristos json_int ret; 13195d681e99Schristos const char * cp; 13205d681e99Schristos char * ep; 1321b8ecfcfeSchristos 13225d681e99Schristos cp = json_object_lookup_primitive(ctx, tid, key); 13235d681e99Schristos if (NULL != cp) { 13245d681e99Schristos ret = strtojint(cp, &ep); 13255d681e99Schristos if (cp != ep && '\0' == *ep) 13265d681e99Schristos return ret; 13275d681e99Schristos } 13285d681e99Schristos return def; 1329b8ecfcfeSchristos } 1330b8ecfcfeSchristos 1331b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 13325d681e99Schristos #if 0 /* currently unused */ 1333b8ecfcfeSchristos static double 1334b8ecfcfeSchristos json_object_lookup_float( 1335b8ecfcfeSchristos const json_ctx * ctx, 1336b8ecfcfeSchristos tok_ref tid, 1337b8ecfcfeSchristos const char * key) 1338b8ecfcfeSchristos { 1339b8ecfcfeSchristos double ret; 13405d681e99Schristos const char * cp; 1341b8ecfcfeSchristos char * ep; 1342b8ecfcfeSchristos 13435d681e99Schristos cp = json_object_lookup_primitive(ctx, tid, key); 13445d681e99Schristos if (NULL != cp) { 13455d681e99Schristos ret = strtod(cp, &ep); 13465d681e99Schristos if (cp != ep && '\0' == *ep) 1347b8ecfcfeSchristos return ret; 13485d681e99Schristos } else { 1349b8ecfcfeSchristos errno = EINVAL; 13505d681e99Schristos } 1351b8ecfcfeSchristos return 0.0; 1352b8ecfcfeSchristos } 13535d681e99Schristos #endif 1354b8ecfcfeSchristos 1355b8ecfcfeSchristos static double 1356b8ecfcfeSchristos json_object_lookup_float_default( 1357b8ecfcfeSchristos const json_ctx * ctx, 1358b8ecfcfeSchristos tok_ref tid, 1359b8ecfcfeSchristos const char * key, 1360b8ecfcfeSchristos double def) 1361b8ecfcfeSchristos { 13625d681e99Schristos double ret; 13635d681e99Schristos const char * cp; 13645d681e99Schristos char * ep; 1365b8ecfcfeSchristos 13665d681e99Schristos cp = json_object_lookup_primitive(ctx, tid, key); 13675d681e99Schristos if (NULL != cp) { 13685d681e99Schristos ret = strtod(cp, &ep); 13695d681e99Schristos if (cp != ep && '\0' == *ep) 13705d681e99Schristos return ret; 13715d681e99Schristos } 13725d681e99Schristos return def; 1373b8ecfcfeSchristos } 1374b8ecfcfeSchristos 1375b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1376b8ecfcfeSchristos 1377b8ecfcfeSchristos static BOOL 1378b8ecfcfeSchristos json_parse_record( 1379b8ecfcfeSchristos json_ctx * ctx, 13805d681e99Schristos char * buf, 13815d681e99Schristos size_t len) 1382b8ecfcfeSchristos { 1383b8ecfcfeSchristos jsmn_parser jsm; 1384b8ecfcfeSchristos int idx, rc; 1385b8ecfcfeSchristos 1386b8ecfcfeSchristos jsmn_init(&jsm); 13875d681e99Schristos rc = jsmn_parse(&jsm, buf, len, ctx->tok, JSMN_MAXTOK); 13885d681e99Schristos if (rc <= 0) 13895d681e99Schristos return FALSE; 1390b8ecfcfeSchristos ctx->buf = buf; 13915d681e99Schristos ctx->ntok = rc; 13925d681e99Schristos 13935d681e99Schristos if (JSMN_OBJECT != ctx->tok[0].type) 13945d681e99Schristos return FALSE; /* not object!?! */ 1395b8ecfcfeSchristos 1396b8ecfcfeSchristos /* Make all tokens NUL terminated by overwriting the 13975d681e99Schristos * terminator symbol. Makes string compares and number parsing a 13985d681e99Schristos * lot easier! 1399b8ecfcfeSchristos */ 14005d681e99Schristos for (idx = 0; idx < ctx->ntok; ++idx) 1401b8ecfcfeSchristos if (ctx->tok[idx].end > ctx->tok[idx].start) 1402b8ecfcfeSchristos ctx->buf[ctx->tok[idx].end] = '\0'; 1403b8ecfcfeSchristos return TRUE; 1404b8ecfcfeSchristos } 1405b8ecfcfeSchristos 1406b8ecfcfeSchristos 1407b8ecfcfeSchristos /* ===================================================================== 1408b8ecfcfeSchristos * static local helpers 1409b8ecfcfeSchristos */ 14105d681e99Schristos static BOOL 14115d681e99Schristos get_binary_time( 14125d681e99Schristos l_fp * const dest , 14135d681e99Schristos json_ctx * const jctx , 14145d681e99Schristos const char * const time_name, 14155d681e99Schristos const char * const frac_name, 14165d681e99Schristos long fscale ) 14175d681e99Schristos { 14185d681e99Schristos BOOL retv = FALSE; 14195d681e99Schristos struct timespec ts; 14205d681e99Schristos 14215d681e99Schristos errno = 0; 14225d681e99Schristos ts.tv_sec = (time_t)json_object_lookup_int(jctx, 0, time_name); 14235d681e99Schristos ts.tv_nsec = (long )json_object_lookup_int(jctx, 0, frac_name); 14245d681e99Schristos if (0 == errno) { 14255d681e99Schristos ts.tv_nsec *= fscale; 14265d681e99Schristos *dest = tspec_stamp_to_lfp(ts); 14275d681e99Schristos retv = TRUE; 14285d681e99Schristos } 14295d681e99Schristos return retv; 14305d681e99Schristos } 1431b8ecfcfeSchristos 1432b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1433b8ecfcfeSchristos /* Process a WATCH record 1434b8ecfcfeSchristos * 1435b8ecfcfeSchristos * Currently this is only used to recognise that the device is present 1436b8ecfcfeSchristos * and that we're listed subscribers. 1437b8ecfcfeSchristos */ 1438b8ecfcfeSchristos static void 1439b8ecfcfeSchristos process_watch( 1440b8ecfcfeSchristos peerT * const peer , 1441b8ecfcfeSchristos json_ctx * const jctx , 1442b8ecfcfeSchristos const l_fp * const rtime) 1443b8ecfcfeSchristos { 1444b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 1445b8ecfcfeSchristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1446b8ecfcfeSchristos 14475d681e99Schristos const char * path; 14485d681e99Schristos 14495d681e99Schristos path = json_object_lookup_string(jctx, 0, "device"); 14505d681e99Schristos if (NULL == path || strcmp(path, up->device)) 14515d681e99Schristos return; 14525d681e99Schristos 14535d681e99Schristos if (json_object_lookup_bool(jctx, 0, "enable") > 0 && 14545d681e99Schristos json_object_lookup_bool(jctx, 0, "json" ) > 0 ) 1455b8ecfcfeSchristos up->fl_watch = -1; 14565d681e99Schristos else 14575d681e99Schristos up->fl_watch = 0; 14585d681e99Schristos DPRINTF(2, ("%s: process_watch, enabled=%d\n", 14595d681e99Schristos up->logname, (up->fl_watch & 1))); 1460b8ecfcfeSchristos } 1461b8ecfcfeSchristos 1462b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1463b8ecfcfeSchristos 1464b8ecfcfeSchristos static void 1465b8ecfcfeSchristos process_version( 1466b8ecfcfeSchristos peerT * const peer , 1467b8ecfcfeSchristos json_ctx * const jctx , 1468b8ecfcfeSchristos const l_fp * const rtime) 1469b8ecfcfeSchristos { 1470b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 1471b8ecfcfeSchristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1472b8ecfcfeSchristos 1473b8ecfcfeSchristos int len; 1474b8ecfcfeSchristos char * buf; 1475b8ecfcfeSchristos const char *revision; 1476b8ecfcfeSchristos const char *release; 14775d681e99Schristos uint16_t pvhi, pvlo; 1478b8ecfcfeSchristos 1479b8ecfcfeSchristos /* get protocol version number */ 1480b8ecfcfeSchristos revision = json_object_lookup_string_default( 1481b8ecfcfeSchristos jctx, 0, "rev", "(unknown)"); 1482b8ecfcfeSchristos release = json_object_lookup_string_default( 1483b8ecfcfeSchristos jctx, 0, "release", "(unknown)"); 1484b8ecfcfeSchristos errno = 0; 14855d681e99Schristos pvhi = (uint16_t)json_object_lookup_int(jctx, 0, "proto_major"); 14865d681e99Schristos pvlo = (uint16_t)json_object_lookup_int(jctx, 0, "proto_minor"); 14875d681e99Schristos 1488b8ecfcfeSchristos if (0 == errno) { 14895d681e99Schristos if ( ! up->fl_vers) 1490b8ecfcfeSchristos msyslog(LOG_INFO, 1491b8ecfcfeSchristos "%s: GPSD revision=%s release=%s protocol=%u.%u", 14925d681e99Schristos up->logname, revision, release, 14935d681e99Schristos pvhi, pvlo); 14945d681e99Schristos up->proto_version = PROTO_VERSION(pvhi, pvlo); 14955d681e99Schristos up->fl_vers = -1; 14965d681e99Schristos } else { 14975d681e99Schristos if (syslogok(pp, up)) 14985d681e99Schristos msyslog(LOG_INFO, 14995d681e99Schristos "%s: could not evaluate version data", 15005d681e99Schristos up->logname); 15015d681e99Schristos return; 1502b8ecfcfeSchristos } 15035d681e99Schristos /* With the 3.9 GPSD protocol, '*_musec' vanished from the PPS 15045d681e99Schristos * record and was replace by '*_nsec'. 1505b8ecfcfeSchristos */ 15065d681e99Schristos up->pf_nsec = -(up->proto_version >= PROTO_VERSION(3,9)); 1507b8ecfcfeSchristos 15085d681e99Schristos /* With the 3.10 protocol we can get TOFF records for better 15095d681e99Schristos * timing information. 15105d681e99Schristos */ 15115d681e99Schristos up->pf_toff = -(up->proto_version >= PROTO_VERSION(3,10)); 1512b8ecfcfeSchristos 15135d681e99Schristos /* request watch for our GPS device if not yet watched. 15145d681e99Schristos * 15155d681e99Schristos * The version string is also sent as a life signal, if we have 15165d681e99Schristos * seen useable data. So if we're already watching the device, 15175d681e99Schristos * skip the request. 15185d681e99Schristos * 1519b8ecfcfeSchristos * Reuse the input buffer, which is no longer needed in the 1520b8ecfcfeSchristos * current cycle. Also assume that we can write the watch 1521b8ecfcfeSchristos * request in one sweep into the socket; since we do not do 1522b8ecfcfeSchristos * output otherwise, this should always work. (Unless the 1523b8ecfcfeSchristos * TCP/IP window size gets lower than the length of the 1524b8ecfcfeSchristos * request. We handle that when it happens.) 1525b8ecfcfeSchristos */ 15265d681e99Schristos if (up->fl_watch) 15275d681e99Schristos return; 15285d681e99Schristos 152968dbbb44Schristos /* The logon string is actually the ?WATCH command of GPSD, 153068dbbb44Schristos * using JSON data and selecting the GPS device name we created 1531*eabc0478Schristos * from our unit number. We have an old and a newer version that 153268dbbb44Schristos * request PPS (and TOFF) transmission. 15335d681e99Schristos */ 1534b8ecfcfeSchristos snprintf(up->buffer, sizeof(up->buffer), 15355d681e99Schristos "?WATCH={\"device\":\"%s\",\"enable\":true,\"json\":true%s};\r\n", 153668dbbb44Schristos up->device, (up->pf_toff ? ",\"pps\":true" : "")); 1537b8ecfcfeSchristos buf = up->buffer; 1538b8ecfcfeSchristos len = strlen(buf); 1539*eabc0478Schristos log_data(peer, 2, "send", buf, len); 15405d681e99Schristos if (len != write(pp->io.fd, buf, len) && (syslogok(pp, up))) { 1541b8ecfcfeSchristos /* Note: if the server fails to read our request, the 1542b8ecfcfeSchristos * resulting data timeout will take care of the 1543b8ecfcfeSchristos * connection! 1544b8ecfcfeSchristos */ 15455d681e99Schristos msyslog(LOG_ERR, "%s: failed to write watch request (%m)", 15465d681e99Schristos up->logname); 1547b8ecfcfeSchristos } 1548b8ecfcfeSchristos } 1549b8ecfcfeSchristos 1550b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1551b8ecfcfeSchristos 1552b8ecfcfeSchristos static void 1553b8ecfcfeSchristos process_tpv( 1554b8ecfcfeSchristos peerT * const peer , 1555b8ecfcfeSchristos json_ctx * const jctx , 1556b8ecfcfeSchristos const l_fp * const rtime) 1557b8ecfcfeSchristos { 1558b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 1559b8ecfcfeSchristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1560b8ecfcfeSchristos 1561b8ecfcfeSchristos const char * gps_time; 1562b8ecfcfeSchristos int gps_mode; 15635d681e99Schristos double ept; 156422b6c4fcSchristos int xlog2; 1565b8ecfcfeSchristos 1566b8ecfcfeSchristos gps_mode = (int)json_object_lookup_int_default( 1567b8ecfcfeSchristos jctx, 0, "mode", 0); 1568b8ecfcfeSchristos 15695d681e99Schristos gps_time = json_object_lookup_string( 15705d681e99Schristos jctx, 0, "time"); 1571b8ecfcfeSchristos 15725d681e99Schristos /* accept time stamps only in 2d or 3d fix */ 15735d681e99Schristos if (gps_mode < 2 || NULL == gps_time) { 1574b8ecfcfeSchristos /* receiver has no fix; tell about and avoid stale data */ 15755d681e99Schristos if ( ! up->pf_toff) 15765d681e99Schristos ++up->tc_sti_recv; 15775d681e99Schristos ++up->tc_nosync; 15785d681e99Schristos up->fl_sti = 0; 1579b8ecfcfeSchristos up->fl_pps = 0; 15805d681e99Schristos up->fl_nosync = -1; 1581b8ecfcfeSchristos return; 1582b8ecfcfeSchristos } 15835d681e99Schristos up->fl_nosync = 0; 1584b8ecfcfeSchristos 15855d681e99Schristos /* convert clock and set resulting ref time, but only if the 15865d681e99Schristos * TOFF sentence is *not* available 15875d681e99Schristos */ 15885d681e99Schristos if ( ! up->pf_toff) { 15895d681e99Schristos ++up->tc_sti_recv; 1590b8ecfcfeSchristos /* save last time code to clock data */ 1591b8ecfcfeSchristos save_ltc(pp, gps_time); 15925d681e99Schristos /* now parse the time string */ 15935d681e99Schristos if (convert_ascii_time(&up->sti_stamp, gps_time)) { 15945d681e99Schristos DPRINTF(2, ("%s: process_tpv, stamp='%s'," 15955d681e99Schristos " recvt='%s' mode=%u\n", 15965d681e99Schristos up->logname, 15975d681e99Schristos gmprettydate(&up->sti_stamp), 15985d681e99Schristos gmprettydate(&up->sti_recvt), 1599b8ecfcfeSchristos gps_mode)); 1600b8ecfcfeSchristos 16015d681e99Schristos /* have to use local receive time as substitute 16025d681e99Schristos * for the real receive time: TPV does not tell 16035d681e99Schristos * us. 16045d681e99Schristos */ 16055d681e99Schristos up->sti_local = *rtime; 16065d681e99Schristos up->sti_recvt = *rtime; 16075d681e99Schristos L_SUB(&up->sti_recvt, &up->sti_fudge); 16085d681e99Schristos up->fl_sti = -1; 1609b8ecfcfeSchristos } else { 16105d681e99Schristos ++up->tc_breply; 16115d681e99Schristos up->fl_sti = 0; 16125d681e99Schristos } 1613b8ecfcfeSchristos } 1614b8ecfcfeSchristos 1615b8ecfcfeSchristos /* Set the precision from the GPSD data 16165d681e99Schristos * Use the ETP field for an estimation of the precision of the 16175d681e99Schristos * serial data. If ETP is not available, use the default serial 16185d681e99Schristos * data presion instead. (Note: The PPS branch has a different 16195d681e99Schristos * precision estimation, since it gets the proper value directly 16205d681e99Schristos * from GPSD!) 1621b8ecfcfeSchristos */ 16225d681e99Schristos ept = json_object_lookup_float_default(jctx, 0, "ept", 2.0e-3); 16235d681e99Schristos ept = frexp(fabs(ept)*0.70710678, &xlog2); /* ~ sqrt(0.5) */ 16245d681e99Schristos if (ept < 0.25) 16255d681e99Schristos xlog2 = INT_MIN; 16265d681e99Schristos if (ept > 2.0) 16275d681e99Schristos xlog2 = INT_MAX; 16285d681e99Schristos up->sti_prec = clamped_precision(xlog2); 1629b8ecfcfeSchristos } 1630b8ecfcfeSchristos 1631b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1632b8ecfcfeSchristos 1633b8ecfcfeSchristos static void 1634b8ecfcfeSchristos process_pps( 1635b8ecfcfeSchristos peerT * const peer , 1636b8ecfcfeSchristos json_ctx * const jctx , 1637b8ecfcfeSchristos const l_fp * const rtime) 1638b8ecfcfeSchristos { 1639b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 1640b8ecfcfeSchristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1641b8ecfcfeSchristos 16425d681e99Schristos int xlog2; 1643b8ecfcfeSchristos 16445d681e99Schristos ++up->tc_pps_recv; 1645b8ecfcfeSchristos 16465d681e99Schristos /* Bail out if there's indication that time sync is bad or 16475d681e99Schristos * if we're explicitely requested to ignore PPS data. 16485d681e99Schristos */ 16495d681e99Schristos if (up->fl_nosync) 16505d681e99Schristos return; 1651b8ecfcfeSchristos 1652b8ecfcfeSchristos up->pps_local = *rtime; 16535d681e99Schristos /* Now grab the time values. 'clock_*' is the event time of the 16545d681e99Schristos * pulse measured on the local system clock; 'real_*' is the GPS 16555d681e99Schristos * reference time GPSD associated with the pulse. 16565d681e99Schristos */ 16575d681e99Schristos if (up->pf_nsec) { 16585d681e99Schristos if ( ! get_binary_time(&up->pps_recvt2, jctx, 16595d681e99Schristos "clock_sec", "clock_nsec", 1)) 16605d681e99Schristos goto fail; 16615d681e99Schristos if ( ! get_binary_time(&up->pps_stamp2, jctx, 16625d681e99Schristos "real_sec", "real_nsec", 1)) 16635d681e99Schristos goto fail; 16645d681e99Schristos } else { 16655d681e99Schristos if ( ! get_binary_time(&up->pps_recvt2, jctx, 16665d681e99Schristos "clock_sec", "clock_musec", 1000)) 16675d681e99Schristos goto fail; 16685d681e99Schristos if ( ! get_binary_time(&up->pps_stamp2, jctx, 16695d681e99Schristos "real_sec", "real_musec", 1000)) 16705d681e99Schristos goto fail; 16715d681e99Schristos } 1672b8ecfcfeSchristos 16735d681e99Schristos /* Try to read the precision field from the PPS record. If it's 16745d681e99Schristos * not there, take the precision from the serial data. 16755d681e99Schristos */ 16765d681e99Schristos xlog2 = json_object_lookup_int_default( 16775d681e99Schristos jctx, 0, "precision", up->sti_prec); 16785d681e99Schristos up->pps_prec = clamped_precision(xlog2); 16795d681e99Schristos 16805d681e99Schristos /* Get fudged receive times for primary & secondary unit */ 16815d681e99Schristos up->pps_recvt = up->pps_recvt2; 16825d681e99Schristos L_SUB(&up->pps_recvt , &up->pps_fudge ); 16835d681e99Schristos L_SUB(&up->pps_recvt2, &up->pps_fudge2); 16845d681e99Schristos pp->lastrec = up->pps_recvt; 16855d681e99Schristos 16865d681e99Schristos /* Map to nearest full second as reference time stamp for the 16875d681e99Schristos * primary channel. Sanity checks are done in evaluation step. 16885d681e99Schristos */ 1689b8ecfcfeSchristos up->pps_stamp = up->pps_recvt; 1690b8ecfcfeSchristos L_ADDUF(&up->pps_stamp, 0x80000000u); 1691b8ecfcfeSchristos up->pps_stamp.l_uf = 0; 1692b8ecfcfeSchristos 16935d681e99Schristos if (NULL != up->pps_peer) 16945d681e99Schristos save_ltc(up->pps_peer->procptr, 16955d681e99Schristos gmprettydate(&up->pps_stamp2)); 16965d681e99Schristos DPRINTF(2, ("%s: PPS record processed," 16975d681e99Schristos " stamp='%s', recvt='%s'\n", 16985d681e99Schristos up->logname, 16995d681e99Schristos gmprettydate(&up->pps_stamp2), 17005d681e99Schristos gmprettydate(&up->pps_recvt2))); 1701b8ecfcfeSchristos 17025d681e99Schristos up->fl_pps = (0 != (pp->sloppyclockflag & CLK_FLAG2)) - 1; 17035d681e99Schristos up->fl_pps2 = -1; 1704b8ecfcfeSchristos return; 1705b8ecfcfeSchristos 1706b8ecfcfeSchristos fail: 17075d681e99Schristos DPRINTF(1, ("%s: PPS record processing FAILED\n", 17085d681e99Schristos up->logname)); 17095d681e99Schristos ++up->tc_breply; 17105d681e99Schristos } 17115d681e99Schristos 17125d681e99Schristos /* ------------------------------------------------------------------ */ 17135d681e99Schristos 17145d681e99Schristos static void 17155d681e99Schristos process_toff( 17165d681e99Schristos peerT * const peer , 17175d681e99Schristos json_ctx * const jctx , 17185d681e99Schristos const l_fp * const rtime) 17195d681e99Schristos { 17205d681e99Schristos clockprocT * const pp = peer->procptr; 17215d681e99Schristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 17225d681e99Schristos 17235d681e99Schristos ++up->tc_sti_recv; 17245d681e99Schristos 17255d681e99Schristos /* remember this! */ 17265d681e99Schristos up->pf_toff = -1; 17275d681e99Schristos 17285d681e99Schristos /* bail out if there's indication that time sync is bad */ 17295d681e99Schristos if (up->fl_nosync) 17305d681e99Schristos return; 17315d681e99Schristos 17325d681e99Schristos if ( ! get_binary_time(&up->sti_recvt, jctx, 17335d681e99Schristos "clock_sec", "clock_nsec", 1)) 17345d681e99Schristos goto fail; 17355d681e99Schristos if ( ! get_binary_time(&up->sti_stamp, jctx, 17365d681e99Schristos "real_sec", "real_nsec", 1)) 17375d681e99Schristos goto fail; 17385d681e99Schristos L_SUB(&up->sti_recvt, &up->sti_fudge); 17395d681e99Schristos up->sti_local = *rtime; 17405d681e99Schristos up->fl_sti = -1; 17415d681e99Schristos 17425d681e99Schristos save_ltc(pp, gmprettydate(&up->sti_stamp)); 17435d681e99Schristos DPRINTF(2, ("%s: TOFF record processed," 17445d681e99Schristos " stamp='%s', recvt='%s'\n", 17455d681e99Schristos up->logname, 17465d681e99Schristos gmprettydate(&up->sti_stamp), 17475d681e99Schristos gmprettydate(&up->sti_recvt))); 17485d681e99Schristos return; 17495d681e99Schristos 17505d681e99Schristos fail: 17515d681e99Schristos DPRINTF(1, ("%s: TOFF record processing FAILED\n", 17525d681e99Schristos up->logname)); 17535d681e99Schristos ++up->tc_breply; 1754b8ecfcfeSchristos } 1755b8ecfcfeSchristos 1756b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1757b8ecfcfeSchristos 1758b8ecfcfeSchristos static void 1759b8ecfcfeSchristos gpsd_parse( 1760b8ecfcfeSchristos peerT * const peer , 1761b8ecfcfeSchristos const l_fp * const rtime) 1762b8ecfcfeSchristos { 1763b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 1764b8ecfcfeSchristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1765b8ecfcfeSchristos 1766b8ecfcfeSchristos const char * clsid; 1767b8ecfcfeSchristos 17685d681e99Schristos DPRINTF(2, ("%s: gpsd_parse: time %s '%.*s'\n", 17695d681e99Schristos up->logname, ulfptoa(rtime, 6), 17705d681e99Schristos up->buflen, up->buffer)); 1771b8ecfcfeSchristos 17725d681e99Schristos /* See if we can grab anything potentially useful. JSMN does not 17735d681e99Schristos * need a trailing NUL, but it needs the number of bytes to 17745d681e99Schristos * process. */ 17755d681e99Schristos if (!json_parse_record(&up->json_parse, up->buffer, up->buflen)) { 17765d681e99Schristos ++up->tc_breply; 1777b8ecfcfeSchristos return; 17785d681e99Schristos } 1779b8ecfcfeSchristos 1780b8ecfcfeSchristos /* Now dispatch over the objects we know */ 17815d681e99Schristos clsid = json_object_lookup_string(&up->json_parse, 0, "class"); 17825d681e99Schristos if (NULL == clsid) { 17835d681e99Schristos ++up->tc_breply; 17845d681e99Schristos return; 17855d681e99Schristos } 1786b8ecfcfeSchristos 17875d681e99Schristos if (!strcmp("TPV", clsid)) 17885d681e99Schristos process_tpv(peer, &up->json_parse, rtime); 1789b8ecfcfeSchristos else if (!strcmp("PPS", clsid)) 17905d681e99Schristos process_pps(peer, &up->json_parse, rtime); 17915d681e99Schristos else if (!strcmp("TOFF", clsid)) 17925d681e99Schristos process_toff(peer, &up->json_parse, rtime); 17935d681e99Schristos else if (!strcmp("VERSION", clsid)) 17945d681e99Schristos process_version(peer, &up->json_parse, rtime); 1795b8ecfcfeSchristos else if (!strcmp("WATCH", clsid)) 17965d681e99Schristos process_watch(peer, &up->json_parse, rtime); 1797b8ecfcfeSchristos else 1798b8ecfcfeSchristos return; /* nothing we know about... */ 17995d681e99Schristos ++up->tc_recv; 1800b8ecfcfeSchristos 18015d681e99Schristos /* if possible, feed the PPS side channel */ 18025d681e99Schristos if (up->pps_peer) 18035d681e99Schristos eval_pps_secondary( 18045d681e99Schristos up->pps_peer, up->pps_peer->procptr, up); 1805b8ecfcfeSchristos 18065d681e99Schristos /* check PPS vs. STI receive times: 18075d681e99Schristos * If STI is before PPS, then clearly the STI is too old. If PPS 18085d681e99Schristos * is before STI by more than one second, then PPS is too old. 18095d681e99Schristos * Weed out stale time stamps & flags. 18105d681e99Schristos */ 18115d681e99Schristos if (up->fl_pps && up->fl_sti) { 18125d681e99Schristos l_fp diff; 18135d681e99Schristos diff = up->sti_local; 18145d681e99Schristos L_SUB(&diff, &up->pps_local); 18155d681e99Schristos if (diff.l_i > 0) 18165d681e99Schristos up->fl_pps = 0; /* pps too old */ 18175d681e99Schristos else if (diff.l_i < 0) 18185d681e99Schristos up->fl_sti = 0; /* serial data too old */ 1819b8ecfcfeSchristos } 18205d681e99Schristos 18215d681e99Schristos /* dispatch to the mode-dependent processing functions */ 18225d681e99Schristos switch (up->mode) { 18235d681e99Schristos default: 18245d681e99Schristos case MODE_OP_STI: 18255d681e99Schristos eval_serial(peer, pp, up); 18265d681e99Schristos break; 18275d681e99Schristos 18285d681e99Schristos case MODE_OP_STRICT: 18295d681e99Schristos eval_strict(peer, pp, up); 18305d681e99Schristos break; 18315d681e99Schristos 18325d681e99Schristos case MODE_OP_AUTO: 18335d681e99Schristos eval_auto(peer, pp, up); 18345d681e99Schristos break; 1835b8ecfcfeSchristos } 1836b8ecfcfeSchristos } 1837b8ecfcfeSchristos 1838b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1839b8ecfcfeSchristos 1840b8ecfcfeSchristos static void 1841b8ecfcfeSchristos gpsd_stop_socket( 1842b8ecfcfeSchristos peerT * const peer) 1843b8ecfcfeSchristos { 1844b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 1845b8ecfcfeSchristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1846b8ecfcfeSchristos 18475d681e99Schristos if (-1 != pp->io.fd) { 1848b8ecfcfeSchristos if (syslogok(pp, up)) 1849b8ecfcfeSchristos msyslog(LOG_INFO, 18505d681e99Schristos "%s: closing socket to GPSD, fd=%d", 18515d681e99Schristos up->logname, pp->io.fd); 18525d681e99Schristos else 18535d681e99Schristos DPRINTF(1, ("%s: closing socket to GPSD, fd=%d\n", 18545d681e99Schristos up->logname, pp->io.fd)); 18555d681e99Schristos io_closeclock(&pp->io); 18565d681e99Schristos pp->io.fd = -1; 18575d681e99Schristos } 1858b8ecfcfeSchristos up->tickover = up->tickpres; 1859b8ecfcfeSchristos up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH); 1860b8ecfcfeSchristos up->fl_vers = 0; 18615d681e99Schristos up->fl_sti = 0; 1862b8ecfcfeSchristos up->fl_pps = 0; 1863b8ecfcfeSchristos up->fl_watch = 0; 1864b8ecfcfeSchristos } 1865b8ecfcfeSchristos 1866b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1867b8ecfcfeSchristos 1868b8ecfcfeSchristos static void 1869b8ecfcfeSchristos gpsd_init_socket( 1870b8ecfcfeSchristos peerT * const peer) 1871b8ecfcfeSchristos { 1872b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 1873b8ecfcfeSchristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1874b8ecfcfeSchristos addrinfoT * ai; 1875b8ecfcfeSchristos int rc; 1876b8ecfcfeSchristos int ov; 1877b8ecfcfeSchristos 1878b8ecfcfeSchristos /* draw next address to try */ 1879b8ecfcfeSchristos if (NULL == up->addr) 1880b8ecfcfeSchristos up->addr = s_gpsd_addr; 1881b8ecfcfeSchristos ai = up->addr; 1882b8ecfcfeSchristos up->addr = ai->ai_next; 1883b8ecfcfeSchristos 1884b8ecfcfeSchristos /* try to create a matching socket */ 1885b8ecfcfeSchristos up->fdt = socket( 1886b8ecfcfeSchristos ai->ai_family, ai->ai_socktype, ai->ai_protocol); 1887b8ecfcfeSchristos if (-1 == up->fdt) { 1888b8ecfcfeSchristos if (syslogok(pp, up)) 1889b8ecfcfeSchristos msyslog(LOG_ERR, 1890b8ecfcfeSchristos "%s: cannot create GPSD socket: %m", 18915d681e99Schristos up->logname); 1892b8ecfcfeSchristos goto no_socket; 1893b8ecfcfeSchristos } 1894b8ecfcfeSchristos 18955d681e99Schristos /* Make sure the socket is non-blocking. Connect/reconnect and 18965d681e99Schristos * IO happen in an event-driven environment, and synchronous 18975d681e99Schristos * operations wreak havoc on that. 18985d681e99Schristos */ 1899b8ecfcfeSchristos rc = fcntl(up->fdt, F_SETFL, O_NONBLOCK, 1); 1900b8ecfcfeSchristos if (-1 == rc) { 1901b8ecfcfeSchristos if (syslogok(pp, up)) 1902b8ecfcfeSchristos msyslog(LOG_ERR, 1903b8ecfcfeSchristos "%s: cannot set GPSD socket to non-blocking: %m", 19045d681e99Schristos up->logname); 1905b8ecfcfeSchristos goto no_socket; 1906b8ecfcfeSchristos } 19075d681e99Schristos /* Disable nagling. The way both GPSD and NTPD handle the 19085d681e99Schristos * protocol makes it record-oriented, and in most cases 19095d681e99Schristos * complete records (JSON serialised objects) will be sent in 19105d681e99Schristos * one sweep. Nagling gives not much advantage but adds another 19115d681e99Schristos * delay, which can worsen the situation for some packets. 19125d681e99Schristos */ 1913b8ecfcfeSchristos ov = 1; 1914b8ecfcfeSchristos rc = setsockopt(up->fdt, IPPROTO_TCP, TCP_NODELAY, 19154eea345dSchristos (void *)&ov, sizeof(ov)); 1916b8ecfcfeSchristos if (-1 == rc) { 1917b8ecfcfeSchristos if (syslogok(pp, up)) 1918b8ecfcfeSchristos msyslog(LOG_INFO, 1919b8ecfcfeSchristos "%s: cannot disable TCP nagle: %m", 19205d681e99Schristos up->logname); 1921b8ecfcfeSchristos } 1922b8ecfcfeSchristos 19235d681e99Schristos /* Start a non-blocking connect. There might be a synchronous 19245d681e99Schristos * connection result we have to handle. 19255d681e99Schristos */ 1926b8ecfcfeSchristos rc = connect(up->fdt, ai->ai_addr, ai->ai_addrlen); 19275d681e99Schristos if (-1 == rc) { 19285d681e99Schristos if (errno == EINPROGRESS) { 19295d681e99Schristos DPRINTF(1, ("%s: async connect pending, fd=%d\n", 19305d681e99Schristos up->logname, up->fdt)); 19315d681e99Schristos return; 19325d681e99Schristos } 19335d681e99Schristos 1934b8ecfcfeSchristos if (syslogok(pp, up)) 1935b8ecfcfeSchristos msyslog(LOG_ERR, 1936b8ecfcfeSchristos "%s: cannot connect GPSD socket: %m", 19375d681e99Schristos up->logname); 19385d681e99Schristos goto no_socket; 19395d681e99Schristos } 19405d681e99Schristos 19415d681e99Schristos /* We had a successful synchronous connect, so we add the 19425d681e99Schristos * refclock processing ASAP. We still have to wait for the 19435d681e99Schristos * version string and apply the watch command later on, but we 19445d681e99Schristos * might as well get the show on the road now. 19455d681e99Schristos */ 19465d681e99Schristos DPRINTF(1, ("%s: new socket connection, fd=%d\n", 19475d681e99Schristos up->logname, up->fdt)); 19485d681e99Schristos 19495d681e99Schristos pp->io.fd = up->fdt; 19505d681e99Schristos up->fdt = -1; 19515d681e99Schristos if (0 == io_addclock(&pp->io)) { 19525d681e99Schristos if (syslogok(pp, up)) 19535d681e99Schristos msyslog(LOG_ERR, 19545d681e99Schristos "%s: failed to register with I/O engine", 19555d681e99Schristos up->logname); 1956b8ecfcfeSchristos goto no_socket; 1957b8ecfcfeSchristos } 1958b8ecfcfeSchristos 1959b8ecfcfeSchristos return; 1960b8ecfcfeSchristos 1961b8ecfcfeSchristos no_socket: 19625d681e99Schristos if (-1 != pp->io.fd) 19635d681e99Schristos close(pp->io.fd); 1964b8ecfcfeSchristos if (-1 != up->fdt) 1965b8ecfcfeSchristos close(up->fdt); 19665d681e99Schristos pp->io.fd = -1; 1967b8ecfcfeSchristos up->fdt = -1; 1968b8ecfcfeSchristos up->tickover = up->tickpres; 1969b8ecfcfeSchristos up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH); 1970b8ecfcfeSchristos } 1971b8ecfcfeSchristos 1972b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 1973b8ecfcfeSchristos 1974b8ecfcfeSchristos static void 1975b8ecfcfeSchristos gpsd_test_socket( 1976b8ecfcfeSchristos peerT * const peer) 1977b8ecfcfeSchristos { 1978b8ecfcfeSchristos clockprocT * const pp = peer->procptr; 1979b8ecfcfeSchristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1980b8ecfcfeSchristos 1981b8ecfcfeSchristos int ec, rc; 1982b8ecfcfeSchristos socklen_t lc; 1983b8ecfcfeSchristos 1984b8ecfcfeSchristos /* Check if the non-blocking connect was finished by testing the 1985b8ecfcfeSchristos * socket for writeability. Use the 'poll()' API if available 1986b8ecfcfeSchristos * and 'select()' otherwise. 1987b8ecfcfeSchristos */ 19885d681e99Schristos DPRINTF(2, ("%s: check connect, fd=%d\n", 19895d681e99Schristos up->logname, up->fdt)); 1990b8ecfcfeSchristos 1991b8ecfcfeSchristos #if defined(HAVE_SYS_POLL_H) 1992b8ecfcfeSchristos { 1993b8ecfcfeSchristos struct pollfd pfd; 1994b8ecfcfeSchristos 1995b8ecfcfeSchristos pfd.events = POLLOUT; 1996b8ecfcfeSchristos pfd.fd = up->fdt; 1997b8ecfcfeSchristos rc = poll(&pfd, 1, 0); 1998b8ecfcfeSchristos if (1 != rc || !(pfd.revents & POLLOUT)) 1999b8ecfcfeSchristos return; 2000b8ecfcfeSchristos } 2001b8ecfcfeSchristos #elif defined(HAVE_SYS_SELECT_H) 2002b8ecfcfeSchristos { 2003b8ecfcfeSchristos struct timeval tout; 2004b8ecfcfeSchristos fd_set wset; 2005b8ecfcfeSchristos 2006b8ecfcfeSchristos memset(&tout, 0, sizeof(tout)); 2007b8ecfcfeSchristos FD_ZERO(&wset); 2008b8ecfcfeSchristos FD_SET(up->fdt, &wset); 2009b8ecfcfeSchristos rc = select(up->fdt+1, NULL, &wset, NULL, &tout); 2010b8ecfcfeSchristos if (0 == rc || !(FD_ISSET(up->fdt, &wset))) 2011b8ecfcfeSchristos return; 2012b8ecfcfeSchristos } 2013b8ecfcfeSchristos #else 2014b8ecfcfeSchristos # error Blooper! That should have been found earlier! 2015b8ecfcfeSchristos #endif 2016b8ecfcfeSchristos 2017b8ecfcfeSchristos /* next timeout is a full one... */ 2018b8ecfcfeSchristos up->tickover = TICKOVER_LOW; 2019b8ecfcfeSchristos 2020b8ecfcfeSchristos /* check for socket error */ 2021b8ecfcfeSchristos ec = 0; 2022b8ecfcfeSchristos lc = sizeof(ec); 20234eea345dSchristos rc = getsockopt(up->fdt, SOL_SOCKET, SO_ERROR, (void *)&ec, &lc); 2024b8ecfcfeSchristos if (-1 == rc || 0 != ec) { 20255d681e99Schristos const char *errtxt; 20265d681e99Schristos if (0 == ec) 20275d681e99Schristos ec = errno; 20285d681e99Schristos errtxt = strerror(ec); 2029b8ecfcfeSchristos if (syslogok(pp, up)) 2030b8ecfcfeSchristos msyslog(LOG_ERR, 20315d681e99Schristos "%s: async connect to GPSD failed," 20325d681e99Schristos " fd=%d, ec=%d(%s)", 20335d681e99Schristos up->logname, up->fdt, ec, errtxt); 20345d681e99Schristos else 20355d681e99Schristos DPRINTF(1, ("%s: async connect to GPSD failed," 20365d681e99Schristos " fd=%d, ec=%d(%s)\n", 20375d681e99Schristos up->logname, up->fdt, ec, errtxt)); 2038b8ecfcfeSchristos goto no_socket; 20395d681e99Schristos } else { 20405d681e99Schristos DPRINTF(1, ("%s: async connect to GPSD succeeded, fd=%d\n", 20415d681e99Schristos up->logname, up->fdt)); 2042b8ecfcfeSchristos } 20435d681e99Schristos 2044b8ecfcfeSchristos /* swap socket FDs, and make sure the clock was added */ 2045b8ecfcfeSchristos pp->io.fd = up->fdt; 2046b8ecfcfeSchristos up->fdt = -1; 2047b8ecfcfeSchristos if (0 == io_addclock(&pp->io)) { 2048b8ecfcfeSchristos if (syslogok(pp, up)) 2049b8ecfcfeSchristos msyslog(LOG_ERR, 2050b8ecfcfeSchristos "%s: failed to register with I/O engine", 20515d681e99Schristos up->logname); 2052b8ecfcfeSchristos goto no_socket; 2053b8ecfcfeSchristos } 2054b8ecfcfeSchristos return; 2055b8ecfcfeSchristos 2056b8ecfcfeSchristos no_socket: 20575d681e99Schristos if (-1 != up->fdt) { 20585d681e99Schristos DPRINTF(1, ("%s: closing socket, fd=%d\n", 20595d681e99Schristos up->logname, up->fdt)); 2060b8ecfcfeSchristos close(up->fdt); 20615d681e99Schristos } 2062b8ecfcfeSchristos up->fdt = -1; 2063b8ecfcfeSchristos up->tickover = up->tickpres; 2064b8ecfcfeSchristos up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH); 2065b8ecfcfeSchristos } 2066b8ecfcfeSchristos 2067b8ecfcfeSchristos /* ===================================================================== 2068b8ecfcfeSchristos * helper stuff 2069b8ecfcfeSchristos */ 2070b8ecfcfeSchristos 20715d681e99Schristos /* ------------------------------------------------------------------- 20725d681e99Schristos * store a properly clamped precision value 2073b8ecfcfeSchristos */ 20745d681e99Schristos static int16_t 20755d681e99Schristos clamped_precision( 20765d681e99Schristos int rawprec) 2077b8ecfcfeSchristos { 20785d681e99Schristos if (rawprec > 0) 20795d681e99Schristos rawprec = 0; 20805d681e99Schristos if (rawprec < -32) 20815d681e99Schristos rawprec = -32; 20825d681e99Schristos return (int16_t)rawprec; 2083b8ecfcfeSchristos } 2084b8ecfcfeSchristos 2085b8ecfcfeSchristos /* ------------------------------------------------------------------- 20865d681e99Schristos * Convert a GPSD timestamp (ISO8601 Format) to an l_fp 2087b8ecfcfeSchristos */ 2088b8ecfcfeSchristos static BOOL 2089b8ecfcfeSchristos convert_ascii_time( 2090b8ecfcfeSchristos l_fp * fp , 2091b8ecfcfeSchristos const char * gps_time) 2092b8ecfcfeSchristos { 2093b8ecfcfeSchristos char *ep; 2094b8ecfcfeSchristos struct tm gd; 2095b8ecfcfeSchristos struct timespec ts; 20965d681e99Schristos uint32_t dw; 2097b8ecfcfeSchristos 2098b8ecfcfeSchristos /* Use 'strptime' to take the brunt of the work, then parse 2099b8ecfcfeSchristos * the fractional part manually, starting with a digit weight of 2100b8ecfcfeSchristos * 10^8 nanoseconds. 2101b8ecfcfeSchristos */ 2102b8ecfcfeSchristos ts.tv_nsec = 0; 2103b8ecfcfeSchristos ep = strptime(gps_time, "%Y-%m-%dT%H:%M:%S", &gd); 21045d681e99Schristos if (NULL == ep) 21055d681e99Schristos return FALSE; /* could not parse the mandatory stuff! */ 2106b8ecfcfeSchristos if (*ep == '.') { 21075d681e99Schristos dw = 100000000u; 2108af12ab5eSchristos while (isdigit(*(u_char*)++ep)) { 2109af12ab5eSchristos ts.tv_nsec += (*(u_char*)ep - '0') * dw; 21105d681e99Schristos dw /= 10u; 2111b8ecfcfeSchristos } 2112b8ecfcfeSchristos } 2113b8ecfcfeSchristos if (ep[0] != 'Z' || ep[1] != '\0') 21145d681e99Schristos return FALSE; /* trailing garbage */ 2115b8ecfcfeSchristos 21165d681e99Schristos /* Now convert the whole thing into a 'l_fp'. We do not use 21175d681e99Schristos * 'mkgmtime()' since its not standard and going through the 21185d681e99Schristos * calendar routines is not much effort, either. 21195d681e99Schristos */ 2120b8ecfcfeSchristos ts.tv_sec = (ntpcal_tm_to_rd(&gd) - DAY_NTP_STARTS) * SECSPERDAY 2121b8ecfcfeSchristos + ntpcal_tm_to_daysec(&gd); 2122b8ecfcfeSchristos *fp = tspec_intv_to_lfp(ts); 2123b8ecfcfeSchristos 2124b8ecfcfeSchristos return TRUE; 2125b8ecfcfeSchristos } 2126b8ecfcfeSchristos 2127b8ecfcfeSchristos /* ------------------------------------------------------------------- 2128b8ecfcfeSchristos * Save the last timecode string, making sure it's properly truncated 2129b8ecfcfeSchristos * if necessary and NUL terminated in any case. 2130b8ecfcfeSchristos */ 2131b8ecfcfeSchristos static void 2132b8ecfcfeSchristos save_ltc( 2133b8ecfcfeSchristos clockprocT * const pp, 2134b8ecfcfeSchristos const char * const tc) 2135b8ecfcfeSchristos { 2136ccc794f0Schristos size_t len = 0; 2137b8ecfcfeSchristos 2138ccc794f0Schristos if (tc) { 2139ccc794f0Schristos len = strlen(tc); 2140b8ecfcfeSchristos if (len >= sizeof(pp->a_lastcode)) 2141b8ecfcfeSchristos len = sizeof(pp->a_lastcode) - 1; 2142b8ecfcfeSchristos memcpy(pp->a_lastcode, tc, len); 2143ccc794f0Schristos } 2144ccc794f0Schristos pp->lencode = (u_short)len; 2145b8ecfcfeSchristos pp->a_lastcode[len] = '\0'; 2146b8ecfcfeSchristos } 2147b8ecfcfeSchristos 21485d681e99Schristos /* ------------------------------------------------------------------- 2149b8ecfcfeSchristos * asprintf replacement... it's not available everywhere... 2150b8ecfcfeSchristos */ 2151b8ecfcfeSchristos static int 2152b8ecfcfeSchristos myasprintf( 2153b8ecfcfeSchristos char ** spp, 2154b8ecfcfeSchristos char const * fmt, 2155b8ecfcfeSchristos ... ) 2156b8ecfcfeSchristos { 2157b8ecfcfeSchristos size_t alen, plen; 2158b8ecfcfeSchristos 2159b8ecfcfeSchristos alen = 32; 2160b8ecfcfeSchristos *spp = NULL; 2161b8ecfcfeSchristos do { 2162b8ecfcfeSchristos va_list va; 2163b8ecfcfeSchristos 2164b8ecfcfeSchristos alen += alen; 2165b8ecfcfeSchristos free(*spp); 2166b8ecfcfeSchristos *spp = (char*)malloc(alen); 2167b8ecfcfeSchristos if (NULL == *spp) 2168b8ecfcfeSchristos return -1; 2169b8ecfcfeSchristos 2170b8ecfcfeSchristos va_start(va, fmt); 2171b8ecfcfeSchristos plen = (size_t)vsnprintf(*spp, alen, fmt, va); 2172b8ecfcfeSchristos va_end(va); 2173b8ecfcfeSchristos } while (plen >= alen); 2174b8ecfcfeSchristos 2175b8ecfcfeSchristos return (int)plen; 2176b8ecfcfeSchristos } 2177b8ecfcfeSchristos 21785d681e99Schristos /* ------------------------------------------------------------------- 21795d681e99Schristos * dump a raw data buffer 21805d681e99Schristos */ 21815d681e99Schristos 21825d681e99Schristos static char * 21835d681e99Schristos add_string( 21845d681e99Schristos char *dp, 21855d681e99Schristos char *ep, 21865d681e99Schristos const char *sp) 21875d681e99Schristos { 21885d681e99Schristos while (dp != ep && *sp) 21895d681e99Schristos *dp++ = *sp++; 21905d681e99Schristos return dp; 21915d681e99Schristos } 21925d681e99Schristos 21935d681e99Schristos static void 21945d681e99Schristos log_data( 21955d681e99Schristos peerT *peer, 2196*eabc0478Schristos int level, 21975d681e99Schristos const char *what, 21985d681e99Schristos const char *buf , 21995d681e99Schristos size_t len ) 22005d681e99Schristos { 22015d681e99Schristos /* we're running single threaded with regards to the clocks. */ 22025d681e99Schristos static char s_lbuf[2048]; 22035d681e99Schristos 22045d681e99Schristos clockprocT * const pp = peer->procptr; 22055d681e99Schristos gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 22065d681e99Schristos 2207*eabc0478Schristos if (debug >= level) { 22085d681e99Schristos const char *sptr = buf; 22095d681e99Schristos const char *stop = buf + len; 22105d681e99Schristos char *dptr = s_lbuf; 22115d681e99Schristos char *dtop = s_lbuf + sizeof(s_lbuf) - 1; /* for NUL */ 22125d681e99Schristos 22135d681e99Schristos while (sptr != stop && dptr != dtop) { 2214af12ab5eSchristos u_char uch = (u_char)*sptr++; 2215af12ab5eSchristos if (uch == '\\') { 22165d681e99Schristos dptr = add_string(dptr, dtop, "\\\\"); 2217af12ab5eSchristos } else if (isprint(uch)) { 2218af12ab5eSchristos *dptr++ = (char)uch; 22195d681e99Schristos } else { 22205d681e99Schristos char fbuf[6]; 2221af12ab5eSchristos snprintf(fbuf, sizeof(fbuf), "\\%03o", uch); 22225d681e99Schristos dptr = add_string(dptr, dtop, fbuf); 22235d681e99Schristos } 22245d681e99Schristos } 22255d681e99Schristos *dptr = '\0'; 22265d681e99Schristos mprintf("%s[%s]: '%s'\n", up->logname, what, s_lbuf); 22275d681e99Schristos } 22285d681e99Schristos } 22295d681e99Schristos 2230*eabc0478Schristos 2231b8ecfcfeSchristos #else 2232b8ecfcfeSchristos NONEMPTY_TRANSLATION_UNIT 2233b8ecfcfeSchristos #endif /* REFCLOCK && CLOCK_GPSDJSON */ 2234