refclock_gpsdjson.c revision 290000
1275970Scy/* 2275970Scy * refclock_gpsdjson.c - clock driver as GPSD JSON client 3275970Scy * Juergen Perlinger (perlinger@ntp.org) 4275970Scy * Feb 11, 2014 for the NTP project. 5275970Scy * The contents of 'html/copyright.html' apply. 6275970Scy * 7275970Scy * Heavily inspired by refclock_nmea.c 8275970Scy * 9290000Sglebius * Special thanks to Gary Miller and Hal Murray for their comments and 10290000Sglebius * ideas. 11290000Sglebius * 12275970Scy * Note: This will currently NOT work with Windows due to some 13275970Scy * limitations: 14275970Scy * 15275970Scy * - There is no GPSD for Windows. (There is an unofficial port to 16275970Scy * cygwin, but Windows is not officially supported.) 17275970Scy * 18290000Sglebius * - To work properly, this driver needs PPS and TPV/TOFF sentences 19290000Sglebius * from GPSD. I don't see how the cygwin port should deal with the 20290000Sglebius * PPS signal. 21275970Scy * 22275970Scy * - The device name matching must be done in a different way for 23275970Scy * Windows. (Can be done with COMxx matching, as done for NMEA.) 24275970Scy * 25275970Scy * Apart from those minor hickups, once GPSD has been fully ported to 26290000Sglebius * Windows, there's no reason why this should not work there ;-) If this 27290000Sglebius * is ever to happen at all is a different question. 28290000Sglebius * 29290000Sglebius * --------------------------------------------------------------------- 30290000Sglebius * 31290000Sglebius * This driver works slightly different from most others, as the PPS 32290000Sglebius * information (if available) is also coming from GPSD via the data 33290000Sglebius * connection. This makes using both the PPS data and the serial data 34290000Sglebius * easier, but OTOH it's not possible to use the ATOM driver to feed a 35290000Sglebius * raw PPS stream to the core of NTPD. 36290000Sglebius * 37290000Sglebius * To go around this, the driver can use a secondary clock unit 38290000Sglebius * (units>=128) that operate in tandem with the primary clock unit 39290000Sglebius * (unit%128). The primary clock unit does all the IO stuff and data 40290000Sglebius * decoding; if a a secondary unit is attached to a primary unit, this 41290000Sglebius * secondary unit is feed with the PPS samples only and can act as a PPS 42290000Sglebius * source to the clock selection. 43290000Sglebius * 44290000Sglebius * The drawback is that the primary unit must be present for the 45290000Sglebius * secondary unit to work. 46290000Sglebius * 47290000Sglebius * This design is a compromise to reduce the IO load for both NTPD and 48290000Sglebius * GPSD; it also ensures that data is transmitted and evaluated only 49290000Sglebius * once on the side of NTPD. 50290000Sglebius * 51290000Sglebius * --------------------------------------------------------------------- 52290000Sglebius * 53290000Sglebius * trouble shooting hints: 54290000Sglebius * 55290000Sglebius * Enable and check the clock stats. Check if there are bad replies; 56290000Sglebius * there should be none. If there are actually bad replies, then the 57290000Sglebius * driver cannot parse all JSON records from GPSD, and some record 58290000Sglebius * types are vital for the operation of the driver. This indicates a 59290000Sglebius * problem on the protocol level. 60290000Sglebius * 61290000Sglebius * When started on the command line with a debug level >= 2, the 62290000Sglebius * driver dumps the raw received data and the parser input to 63290000Sglebius * stdout. Since the debug level is global, NTPD starts to create a 64290000Sglebius * *lot* of output. It makes sense to pipe it through '(f)grep 65290000Sglebius * GPSD_JSON' before writing the result to disk. 66290000Sglebius * 67290000Sglebius * A bit less intrusive is using netcat or telnet to connect to GPSD 68290000Sglebius * and snoop what NTPD would get. If you try this, you have to send a 69290000Sglebius * WATCH command to GPSD: 70290000Sglebius * 71290000Sglebius * ?WATCH={"device":"/dev/gps0","enable":true,"json":true,"pps":true};<CRLF> 72290000Sglebius * 73290000Sglebius * should show you what GPSD has to say to NTPD. Replace "/dev/gps0" 74290000Sglebius * with the device link used by GPSD, if necessary. 75275970Scy */ 76275970Scy 77290000Sglebius 78275970Scy#ifdef HAVE_CONFIG_H 79275970Scy#include <config.h> 80275970Scy#endif 81275970Scy 82275970Scy#include "ntp_types.h" 83275970Scy 84290000Sglebius#if defined(REFCLOCK) && defined(CLOCK_GPSDJSON) && !defined(SYS_WINNT) 85275970Scy 86275970Scy/* ===================================================================== 87290000Sglebius * Get the little JSMN library directly into our guts. Use the 'parent 88290000Sglebius * link' feature for maximum speed. 89275970Scy */ 90290000Sglebius#define JSMN_PARENT_LINKS 91275970Scy#include "../libjsmn/jsmn.c" 92275970Scy 93275970Scy/* ===================================================================== 94290000Sglebius * JSON parsing stuff 95290000Sglebius */ 96290000Sglebius 97290000Sglebius#define JSMN_MAXTOK 350 98290000Sglebius#define INVALID_TOKEN (-1) 99290000Sglebius 100290000Sglebiustypedef struct json_ctx { 101290000Sglebius char * buf; 102290000Sglebius int ntok; 103290000Sglebius jsmntok_t tok[JSMN_MAXTOK]; 104290000Sglebius} json_ctx; 105290000Sglebius 106290000Sglebiustypedef int tok_ref; 107290000Sglebius 108290000Sglebius/* Not all targets have 'long long', and not all of them have 'strtoll'. 109290000Sglebius * Sigh. We roll our own integer number parser. 110290000Sglebius */ 111290000Sglebius#ifdef HAVE_LONG_LONG 112290000Sglebiustypedef signed long long int json_int; 113290000Sglebiustypedef unsigned long long int json_uint; 114290000Sglebius#define JSON_INT_MAX LLONG_MAX 115290000Sglebius#define JSON_INT_MIN LLONG_MIN 116290000Sglebius#else 117290000Sglebiustypedef signed long int json_int; 118290000Sglebiustypedef unsigned long int json_uint; 119290000Sglebius#define JSON_INT_MAX LONG_MAX 120290000Sglebius#define JSON_INT_MIN LONG_MIN 121290000Sglebius#endif 122290000Sglebius 123290000Sglebius/* ===================================================================== 124275970Scy * header stuff we need 125275970Scy */ 126275970Scy 127275970Scy#include <netdb.h> 128275970Scy#include <unistd.h> 129275970Scy#include <fcntl.h> 130275970Scy#include <string.h> 131275970Scy#include <ctype.h> 132290000Sglebius#include <math.h> 133275970Scy 134275970Scy#include <sys/types.h> 135275970Scy#include <sys/socket.h> 136275970Scy#include <sys/stat.h> 137275970Scy#include <netinet/tcp.h> 138275970Scy 139275970Scy#if defined(HAVE_SYS_POLL_H) 140275970Scy# include <sys/poll.h> 141280849Scy#elif defined(HAVE_SYS_SELECT_H) 142275970Scy# include <sys/select.h> 143275970Scy#else 144275970Scy# error need poll() or select() 145275970Scy#endif 146275970Scy 147275970Scy#include "ntpd.h" 148275970Scy#include "ntp_io.h" 149275970Scy#include "ntp_unixtime.h" 150275970Scy#include "ntp_refclock.h" 151275970Scy#include "ntp_stdlib.h" 152275970Scy#include "ntp_calendar.h" 153275970Scy#include "timespecops.h" 154275970Scy 155290000Sglebius/* get operation modes from mode word. 156290000Sglebius 157290000Sglebius * + SERIAL (default) evaluates only serial time information ('STI') as 158290000Sglebius * provided by TPV and TOFF records. TPV evaluation suffers from a 159290000Sglebius * bigger jitter than TOFF, sine it does not contain the receive time 160290000Sglebius * from GPSD and therefore the receive time of NTPD must be 161290000Sglebius * substituted for it. The network latency makes this a second rate 162290000Sglebius * guess. 163290000Sglebius * 164290000Sglebius * If TOFF records are detected in the data stream, the timing 165290000Sglebius * information is gleaned from this record -- it contains the local 166290000Sglebius * receive time stamp from GPSD and therefore eliminates the 167290000Sglebius * transmission latency between GPSD and NTPD. The timing information 168290000Sglebius * from TPV is ignored once a TOFF is detected or expected. 169290000Sglebius * 170290000Sglebius * TPV is still used to check the fix status, so the driver can stop 171290000Sglebius * feeding samples when GPSD says that the time information is 172290000Sglebius * effectively unreliable. 173290000Sglebius * 174290000Sglebius * + STRICT means only feed clock samples when a valid STI/PPS pair is 175290000Sglebius * available. Combines the reference time from STI with the pulse time 176290000Sglebius * from PPS. Masks the serial data jitter as long PPS is available, 177290000Sglebius * but can rapidly deteriorate once PPS drops out. 178290000Sglebius * 179290000Sglebius * + AUTO tries to use STI/PPS pairs if available for some time, and if 180290000Sglebius * this fails for too long switches back to STI only until the PPS 181290000Sglebius * signal becomes available again. See the HTML docs for this driver 182290000Sglebius * about the gotchas and why this is not the default. 183290000Sglebius */ 184290000Sglebius#define MODE_OP_MASK 0x03 185290000Sglebius#define MODE_OP_STI 0 186290000Sglebius#define MODE_OP_STRICT 1 187290000Sglebius#define MODE_OP_AUTO 2 188290000Sglebius#define MODE_OP_MAXVAL 2 189290000Sglebius#define MODE_OP_MODE(x) ((x) & MODE_OP_MASK) 190290000Sglebius 191275970Scy#define PRECISION (-9) /* precision assumed (about 2 ms) */ 192275970Scy#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */ 193275970Scy#define REFID "GPSD" /* reference id */ 194275970Scy#define DESCRIPTION "GPSD JSON client clock" /* who we are */ 195275970Scy 196275970Scy#define MAX_PDU_LEN 1600 197275970Scy#define TICKOVER_LOW 10 198275970Scy#define TICKOVER_HIGH 120 199275970Scy#define LOGTHROTTLE 3600 200275970Scy 201290000Sglebius/* Primary channel PPS avilability dance: 202290000Sglebius * Every good PPS sample gets us a credit of PPS_INCCOUNT points, every 203290000Sglebius * bad/missing PPS sample costs us a debit of PPS_DECCOUNT points. When 204290000Sglebius * the account reaches the upper limit we change to a mode where only 205290000Sglebius * PPS-augmented samples are fed to the core; when the account drops to 206290000Sglebius * zero we switch to a mode where TPV-only timestamps are fed to the 207290000Sglebius * core. 208290000Sglebius * This reduces the chance of rapid alternation between raw and 209290000Sglebius * PPS-augmented time stamps. 210290000Sglebius */ 211290000Sglebius#define PPS_MAXCOUNT 60 /* upper limit of account */ 212290000Sglebius#define PPS_INCCOUNT 3 /* credit for good samples */ 213290000Sglebius#define PPS_DECCOUNT 1 /* debit for bad samples */ 214275970Scy 215290000Sglebius/* The secondary (PPS) channel uses a different strategy to avoid old 216290000Sglebius * PPS samples in the median filter. 217290000Sglebius */ 218290000Sglebius#define PPS2_MAXCOUNT 10 219290000Sglebius 220275970Scy#ifndef BOOL 221275970Scy# define BOOL int 222275970Scy#endif 223275970Scy#ifndef TRUE 224275970Scy# define TRUE 1 225275970Scy#endif 226275970Scy#ifndef FALSE 227275970Scy# define FALSE 0 228275970Scy#endif 229275970Scy 230290000Sglebius#define PROTO_VERSION(hi,lo) \ 231290000Sglebius ((((uint32_t)(hi) << 16) & 0xFFFF0000u) | \ 232290000Sglebius ((uint32_t)(lo) & 0x0FFFFu)) 233290000Sglebius 234290000Sglebius/* some local typedefs: The NTPD formatting style cries for short type 235275970Scy * names, and we provide them locally. Note:the suffix '_t' is reserved 236275970Scy * for the standard; I use a capital T instead. 237275970Scy */ 238275970Scytypedef struct peer peerT; 239275970Scytypedef struct refclockproc clockprocT; 240275970Scytypedef struct addrinfo addrinfoT; 241275970Scy 242275970Scy/* ===================================================================== 243275970Scy * We use the same device name scheme as does the NMEA driver; since 244275970Scy * GPSD supports the same links, we can select devices by a fixed name. 245275970Scy */ 246275970Scystatic const char * s_dev_stem = "/dev/gps"; 247275970Scy 248275970Scy/* ===================================================================== 249275970Scy * forward declarations for transfer vector and the vector itself 250275970Scy */ 251275970Scy 252275970Scystatic void gpsd_init (void); 253275970Scystatic int gpsd_start (int, peerT *); 254275970Scystatic void gpsd_shutdown (int, peerT *); 255275970Scystatic void gpsd_receive (struct recvbuf *); 256275970Scystatic void gpsd_poll (int, peerT *); 257275970Scystatic void gpsd_control (int, const struct refclockstat *, 258275970Scy struct refclockstat *, peerT *); 259275970Scystatic void gpsd_timer (int, peerT *); 260275970Scy 261290000Sglebiusstatic int myasprintf(char**, char const*, ...) NTP_PRINTF(2, 3); 262275970Scy 263290000Sglebiusstatic void enter_opmode(peerT *peer, int mode); 264290000Sglebiusstatic void leave_opmode(peerT *peer, int mode); 265290000Sglebius 266275970Scystruct refclock refclock_gpsdjson = { 267275970Scy gpsd_start, /* start up driver */ 268275970Scy gpsd_shutdown, /* shut down driver */ 269275970Scy gpsd_poll, /* transmit poll message */ 270275970Scy gpsd_control, /* fudge control */ 271275970Scy gpsd_init, /* initialize driver */ 272275970Scy noentry, /* buginfo */ 273275970Scy gpsd_timer /* called once per second */ 274275970Scy}; 275275970Scy 276275970Scy/* ===================================================================== 277275970Scy * our local clock unit and data 278275970Scy */ 279290000Sglebiusstruct gpsd_unit; 280290000Sglebiustypedef struct gpsd_unit gpsd_unitT; 281290000Sglebius 282290000Sglebiusstruct gpsd_unit { 283290000Sglebius /* links for sharing between master/slave units */ 284290000Sglebius gpsd_unitT *next_unit; 285290000Sglebius size_t refcount; 286290000Sglebius 287290000Sglebius /* data for the secondary PPS channel */ 288290000Sglebius peerT *pps_peer; 289290000Sglebius 290290000Sglebius /* unit and operation modes */ 291290000Sglebius int unit; 292290000Sglebius int mode; 293290000Sglebius char *logname; /* cached name for log/print */ 294290000Sglebius char * device; /* device name of unit */ 295290000Sglebius 296275970Scy /* current line protocol version */ 297290000Sglebius uint32_t proto_version; 298275970Scy 299290000Sglebius /* PPS time stamps primary + secondary channel */ 300275970Scy l_fp pps_local; /* when we received the PPS message */ 301275970Scy l_fp pps_stamp; /* related reference time */ 302275970Scy l_fp pps_recvt; /* when GPSD detected the pulse */ 303290000Sglebius l_fp pps_stamp2;/* related reference time (secondary) */ 304290000Sglebius l_fp pps_recvt2;/* when GPSD detected the pulse (secondary)*/ 305290000Sglebius int ppscount; /* PPS counter (primary unit) */ 306290000Sglebius int ppscount2; /* PPS counter (secondary unit) */ 307275970Scy 308290000Sglebius /* TPV or TOFF serial time information */ 309290000Sglebius l_fp sti_local; /* when we received the TPV/TOFF message */ 310290000Sglebius l_fp sti_stamp; /* effective GPS time stamp */ 311290000Sglebius l_fp sti_recvt; /* when GPSD got the fix */ 312275970Scy 313290000Sglebius /* precision estimates */ 314290000Sglebius int16_t sti_prec; /* serial precision based on EPT */ 315290000Sglebius int16_t pps_prec; /* PPS precision from GPSD or above */ 316290000Sglebius 317275970Scy /* fudge values for correction, mirrored as 'l_fp' */ 318290000Sglebius l_fp pps_fudge; /* PPS fudge primary channel */ 319290000Sglebius l_fp pps_fudge2; /* PPS fudge secondary channel */ 320290000Sglebius l_fp sti_fudge; /* TPV/TOFF serial data fudge */ 321275970Scy 322275970Scy /* Flags to indicate available data */ 323290000Sglebius int fl_nosync: 1; /* GPSD signals bad quality */ 324290000Sglebius int fl_sti : 1; /* valid TPV/TOFF seen (have time) */ 325275970Scy int fl_pps : 1; /* valid pulse seen */ 326290000Sglebius int fl_pps2 : 1; /* valid pulse seen for PPS channel */ 327290000Sglebius int fl_rawsti: 1; /* permit raw TPV/TOFF time stamps */ 328275970Scy int fl_vers : 1; /* have protocol version */ 329275970Scy int fl_watch : 1; /* watch reply seen */ 330290000Sglebius /* protocol flags */ 331290000Sglebius int pf_nsec : 1; /* have nanosec PPS info */ 332290000Sglebius int pf_toff : 1; /* have TOFF record for timing */ 333275970Scy 334275970Scy /* admin stuff for sockets and device selection */ 335275970Scy int fdt; /* current connecting socket */ 336275970Scy addrinfoT * addr; /* next address to try */ 337275970Scy u_int tickover; /* timeout countdown */ 338275970Scy u_int tickpres; /* timeout preset */ 339275970Scy 340275970Scy /* tallies for the various events */ 341275970Scy u_int tc_recv; /* received known records */ 342290000Sglebius u_int tc_breply; /* bad replies / parsing errors */ 343290000Sglebius u_int tc_nosync; /* TPV / sample cycles w/o fix */ 344290000Sglebius u_int tc_sti_recv;/* received serial time info records */ 345290000Sglebius u_int tc_sti_used;/* used --^-- */ 346290000Sglebius u_int tc_pps_recv;/* received PPS timing info records */ 347290000Sglebius u_int tc_pps_used;/* used --^-- */ 348275970Scy 349275970Scy /* log bloat throttle */ 350275970Scy u_int logthrottle;/* seconds to next log slot */ 351275970Scy 352290000Sglebius /* The parse context for the current record */ 353290000Sglebius json_ctx json_parse; 354290000Sglebius 355290000Sglebius /* record assemby buffer and saved length */ 356275970Scy int buflen; 357275970Scy char buffer[MAX_PDU_LEN]; 358290000Sglebius}; 359275970Scy 360275970Scy/* ===================================================================== 361275970Scy * static local helpers forward decls 362275970Scy */ 363275970Scystatic void gpsd_init_socket(peerT * const peer); 364275970Scystatic void gpsd_test_socket(peerT * const peer); 365275970Scystatic void gpsd_stop_socket(peerT * const peer); 366275970Scy 367275970Scystatic void gpsd_parse(peerT * const peer, 368275970Scy const l_fp * const rtime); 369275970Scystatic BOOL convert_ascii_time(l_fp * fp, const char * gps_time); 370275970Scystatic void save_ltc(clockprocT * const pp, const char * const tc); 371275970Scystatic int syslogok(clockprocT * const pp, gpsd_unitT * const up); 372290000Sglebiusstatic void log_data(peerT *peer, const char *what, 373290000Sglebius const char *buf, size_t len); 374290000Sglebiusstatic int16_t clamped_precision(int rawprec); 375275970Scy 376275970Scy/* ===================================================================== 377275970Scy * local / static stuff 378275970Scy */ 379275970Scy 380275970Scy/* The logon string is actually the ?WATCH command of GPSD, using JSON 381275970Scy * data and selecting the GPS device name we created from our unit 382290000Sglebius * number. We have an old a newer version that request PPS (and TOFF) 383290000Sglebius * transmission. 384290000Sglebius * Note: These are actually format strings! 385275970Scy */ 386290000Sglebiusstatic const char * const s_req_watch[2] = { 387290000Sglebius "?WATCH={\"device\":\"%s\",\"enable\":true,\"json\":true};\r\n", 388290000Sglebius "?WATCH={\"device\":\"%s\",\"enable\":true,\"json\":true,\"pps\":true};\r\n" 389290000Sglebius}; 390275970Scy 391290000Sglebiusstatic const char * const s_req_version = 392290000Sglebius "?VERSION;\r\n"; 393290000Sglebius 394290000Sglebius/* We keep a static list of network addresses for 'localhost:gpsd' or a 395290000Sglebius * fallback alias of it, and we try to connect to them in round-robin 396290000Sglebius * fashion. The service lookup is done during the driver init 397290000Sglebius * function to minmise the impact of 'getaddrinfo()'. 398290000Sglebius * 399290000Sglebius * Alas, the init function is called even if there are no clocks 400290000Sglebius * configured for this driver. So it makes sense to defer the logging of 401290000Sglebius * any errors or other notifications until the first clock unit is 402290000Sglebius * started -- otherwise there might be syslog entries from a driver that 403290000Sglebius * is not used at all. 404275970Scy */ 405290000Sglebiusstatic addrinfoT *s_gpsd_addr; 406290000Sglebiusstatic gpsd_unitT *s_clock_units; 407275970Scy 408290000Sglebius/* list of service/socket names we want to resolve against */ 409290000Sglebiusstatic const char * const s_svctab[][2] = { 410290000Sglebius { "localhost", "gpsd" }, 411290000Sglebius { "localhost", "2947" }, 412290000Sglebius { "127.0.0.1", "2947" }, 413290000Sglebius { NULL, NULL } 414290000Sglebius}; 415290000Sglebius 416290000Sglebius/* list of address resolution errors and index of service entry that 417290000Sglebius * finally worked. 418290000Sglebius */ 419290000Sglebiusstatic int s_svcerr[sizeof(s_svctab)/sizeof(s_svctab[0])]; 420290000Sglebiusstatic int s_svcidx; 421290000Sglebius 422275970Scy/* ===================================================================== 423275970Scy * log throttling 424275970Scy */ 425275970Scystatic int/*BOOL*/ 426275970Scysyslogok( 427275970Scy clockprocT * const pp, 428275970Scy gpsd_unitT * const up) 429275970Scy{ 430275970Scy int res = (0 != (pp->sloppyclockflag & CLK_FLAG3)) 431275970Scy || (0 == up->logthrottle ) 432275970Scy || (LOGTHROTTLE == up->logthrottle ); 433275970Scy if (res) 434275970Scy up->logthrottle = LOGTHROTTLE; 435275970Scy return res; 436275970Scy} 437275970Scy 438275970Scy/* ===================================================================== 439275970Scy * the clock functions 440275970Scy */ 441275970Scy 442275970Scy/* --------------------------------------------------------------------- 443275970Scy * Init: This currently just gets the socket address for the GPS daemon 444275970Scy */ 445275970Scystatic void 446275970Scygpsd_init(void) 447275970Scy{ 448290000Sglebius addrinfoT hints; 449290000Sglebius int rc, idx; 450290000Sglebius 451290000Sglebius memset(s_svcerr, 0, sizeof(s_svcerr)); 452275970Scy memset(&hints, 0, sizeof(hints)); 453275970Scy hints.ai_family = AF_UNSPEC; 454275970Scy hints.ai_protocol = IPPROTO_TCP; 455275970Scy hints.ai_socktype = SOCK_STREAM; 456275970Scy 457290000Sglebius for (idx = 0; s_svctab[idx][0] && !s_gpsd_addr; idx++) { 458290000Sglebius rc = getaddrinfo(s_svctab[idx][0], s_svctab[idx][1], 459290000Sglebius &hints, &s_gpsd_addr); 460290000Sglebius s_svcerr[idx] = rc; 461290000Sglebius if (0 == rc) 462290000Sglebius break; 463275970Scy s_gpsd_addr = NULL; 464290000Sglebius } 465290000Sglebius s_svcidx = idx; 466275970Scy} 467275970Scy 468275970Scy/* --------------------------------------------------------------------- 469290000Sglebius * Init Check: flush pending log messages and check if we can proceed 470290000Sglebius */ 471290000Sglebiusstatic int/*BOOL*/ 472290000Sglebiusgpsd_init_check(void) 473290000Sglebius{ 474290000Sglebius int idx; 475290000Sglebius 476290000Sglebius /* Check if there is something to log */ 477290000Sglebius if (s_svcidx == 0) 478290000Sglebius return (s_gpsd_addr != NULL); 479290000Sglebius 480290000Sglebius /* spool out the resolver errors */ 481290000Sglebius for (idx = 0; idx < s_svcidx; ++idx) { 482290000Sglebius msyslog(LOG_WARNING, 483290000Sglebius "GPSD_JSON: failed to resolve '%s:%s', rc=%d (%s)", 484290000Sglebius s_svctab[idx][0], s_svctab[idx][1], 485290000Sglebius s_svcerr[idx], gai_strerror(s_svcerr[idx])); 486290000Sglebius } 487290000Sglebius 488290000Sglebius /* check if it was fatal, or if we can proceed */ 489290000Sglebius if (s_gpsd_addr == NULL) 490290000Sglebius msyslog(LOG_ERR, "%s", 491290000Sglebius "GPSD_JSON: failed to get socket address, giving up."); 492290000Sglebius else if (idx != 0) 493290000Sglebius msyslog(LOG_WARNING, 494290000Sglebius "GPSD_JSON: using '%s:%s' instead of '%s:%s'", 495290000Sglebius s_svctab[idx][0], s_svctab[idx][1], 496290000Sglebius s_svctab[0][0], s_svctab[0][1]); 497290000Sglebius 498290000Sglebius /* make sure this gets logged only once and tell if we can 499290000Sglebius * proceed or not 500290000Sglebius */ 501290000Sglebius s_svcidx = 0; 502290000Sglebius return (s_gpsd_addr != NULL); 503290000Sglebius} 504290000Sglebius 505290000Sglebius/* --------------------------------------------------------------------- 506275970Scy * Start: allocate a unit pointer and set up the runtime data 507275970Scy */ 508275970Scystatic int 509275970Scygpsd_start( 510275970Scy int unit, 511275970Scy peerT * peer) 512275970Scy{ 513290000Sglebius clockprocT * const pp = peer->procptr; 514290000Sglebius gpsd_unitT * up; 515290000Sglebius gpsd_unitT ** uscan = &s_clock_units; 516275970Scy 517275970Scy struct stat sb; 518275970Scy 519290000Sglebius /* check if we can proceed at all or if init failed */ 520290000Sglebius if ( ! gpsd_init_check()) 521290000Sglebius return FALSE; 522275970Scy 523290000Sglebius /* search for matching unit */ 524290000Sglebius while ((up = *uscan) != NULL && up->unit != (unit & 0x7F)) 525290000Sglebius uscan = &up->next_unit; 526290000Sglebius if (up == NULL) { 527290000Sglebius /* alloc unit, add to list and increment use count ASAP. */ 528290000Sglebius up = emalloc_zero(sizeof(*up)); 529290000Sglebius *uscan = up; 530290000Sglebius ++up->refcount; 531290000Sglebius 532290000Sglebius /* initialize the unit structure */ 533290000Sglebius up->logname = estrdup(refnumtoa(&peer->srcadr)); 534290000Sglebius up->unit = unit & 0x7F; 535290000Sglebius up->fdt = -1; 536290000Sglebius up->addr = s_gpsd_addr; 537290000Sglebius up->tickpres = TICKOVER_LOW; 538290000Sglebius 539290000Sglebius /* Create the device name and check for a Character 540290000Sglebius * Device. It's assumed that GPSD was started with the 541290000Sglebius * same link, so the names match. (If this is not 542290000Sglebius * practicable, we will have to read the symlink, if 543290000Sglebius * any, so we can get the true device file.) 544290000Sglebius */ 545290000Sglebius if (-1 == myasprintf(&up->device, "%s%u", 546290000Sglebius s_dev_stem, up->unit)) { 547290000Sglebius msyslog(LOG_ERR, "%s: clock device name too long", 548290000Sglebius up->logname); 549290000Sglebius goto dev_fail; 550290000Sglebius } 551290000Sglebius if (-1 == stat(up->device, &sb) || !S_ISCHR(sb.st_mode)) { 552290000Sglebius msyslog(LOG_ERR, "%s: '%s' is not a character device", 553290000Sglebius up->logname, up->device); 554290000Sglebius goto dev_fail; 555290000Sglebius } 556290000Sglebius } else { 557290000Sglebius /* All set up, just increment use count. */ 558290000Sglebius ++up->refcount; 559290000Sglebius } 560290000Sglebius 561275970Scy /* setup refclock processing */ 562275970Scy pp->unitptr = (caddr_t)up; 563290000Sglebius pp->io.fd = -1; 564275970Scy pp->io.clock_recv = gpsd_receive; 565275970Scy pp->io.srcclock = peer; 566275970Scy pp->io.datalen = 0; 567275970Scy pp->a_lastcode[0] = '\0'; 568275970Scy pp->lencode = 0; 569275970Scy pp->clockdesc = DESCRIPTION; 570275970Scy memcpy(&pp->refid, REFID, 4); 571275970Scy 572275970Scy /* Initialize miscellaneous variables */ 573290000Sglebius if (unit >= 128) 574290000Sglebius peer->precision = PPS_PRECISION; 575290000Sglebius else 576290000Sglebius peer->precision = PRECISION; 577275970Scy 578290000Sglebius /* If the daemon name lookup failed, just give up now. */ 579290000Sglebius if (NULL == up->addr) { 580290000Sglebius msyslog(LOG_ERR, "%s: no GPSD socket address, giving up", 581290000Sglebius up->logname); 582290000Sglebius goto dev_fail; 583275970Scy } 584290000Sglebius 585275970Scy LOGIF(CLOCKINFO, 586275970Scy (LOG_NOTICE, "%s: startup, device is '%s'", 587275970Scy refnumtoa(&peer->srcadr), up->device)); 588290000Sglebius up->mode = MODE_OP_MODE(peer->ttl); 589290000Sglebius if (up->mode > MODE_OP_MAXVAL) 590290000Sglebius up->mode = 0; 591290000Sglebius if (unit >= 128) 592290000Sglebius up->pps_peer = peer; 593290000Sglebius else 594290000Sglebius enter_opmode(peer, up->mode); 595275970Scy return TRUE; 596275970Scy 597275970Scydev_fail: 598275970Scy /* On failure, remove all UNIT ressources and declare defeat. */ 599275970Scy 600275970Scy INSIST (up); 601290000Sglebius if (!--up->refcount) { 602290000Sglebius *uscan = up->next_unit; 603290000Sglebius free(up->device); 604290000Sglebius free(up); 605290000Sglebius } 606275970Scy 607275970Scy pp->unitptr = (caddr_t)NULL; 608275970Scy return FALSE; 609275970Scy} 610275970Scy 611275970Scy/* ------------------------------------------------------------------ */ 612275970Scy 613275970Scystatic void 614275970Scygpsd_shutdown( 615275970Scy int unit, 616275970Scy peerT * peer) 617275970Scy{ 618275970Scy clockprocT * const pp = peer->procptr; 619275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 620290000Sglebius gpsd_unitT ** uscan = &s_clock_units; 621275970Scy 622275970Scy UNUSED_ARG(unit); 623275970Scy 624290000Sglebius /* The unit pointer might have been removed already. */ 625290000Sglebius if (up == NULL) 626290000Sglebius return; 627290000Sglebius 628290000Sglebius /* now check if we must close IO resources */ 629290000Sglebius if (peer != up->pps_peer) { 630290000Sglebius if (-1 != pp->io.fd) { 631290000Sglebius DPRINTF(1, ("%s: closing clock, fd=%d\n", 632290000Sglebius up->logname, pp->io.fd)); 633290000Sglebius io_closeclock(&pp->io); 634290000Sglebius pp->io.fd = -1; 635290000Sglebius } 636290000Sglebius if (up->fdt != -1) 637290000Sglebius close(up->fdt); 638275970Scy } 639290000Sglebius /* decrement use count and eventually remove this unit. */ 640290000Sglebius if (!--up->refcount) { 641290000Sglebius /* unlink this unit */ 642290000Sglebius while (*uscan != NULL) 643290000Sglebius if (*uscan == up) 644290000Sglebius *uscan = up->next_unit; 645290000Sglebius else 646290000Sglebius uscan = &(*uscan)->next_unit; 647290000Sglebius free(up->logname); 648290000Sglebius free(up->device); 649290000Sglebius free(up); 650290000Sglebius } 651275970Scy pp->unitptr = (caddr_t)NULL; 652275970Scy LOGIF(CLOCKINFO, 653275970Scy (LOG_NOTICE, "%s: shutdown", refnumtoa(&peer->srcadr))); 654275970Scy} 655275970Scy 656275970Scy/* ------------------------------------------------------------------ */ 657275970Scy 658275970Scystatic void 659275970Scygpsd_receive( 660275970Scy struct recvbuf * rbufp) 661275970Scy{ 662275970Scy /* declare & init control structure ptrs */ 663275970Scy peerT * const peer = rbufp->recv_peer; 664275970Scy clockprocT * const pp = peer->procptr; 665290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 666275970Scy 667275970Scy const char *psrc, *esrc; 668275970Scy char *pdst, *edst, ch; 669275970Scy 670290000Sglebius /* log the data stream, if this is enabled */ 671290000Sglebius log_data(peer, "recv", (const char*)rbufp->recv_buffer, 672290000Sglebius (size_t)rbufp->recv_length); 673290000Sglebius 674290000Sglebius 675275970Scy /* Since we're getting a raw stream data, we must assemble lines 676275970Scy * in our receive buffer. We can't use neither 'refclock_gtraw' 677275970Scy * not 'refclock_gtlin' here... We process chars until we reach 678275970Scy * an EoL (that is, line feed) but we truncate the message if it 679275970Scy * does not fit the buffer. GPSD might truncate messages, too, 680275970Scy * so dealing with truncated buffers is necessary anyway. 681275970Scy */ 682275970Scy psrc = (const char*)rbufp->recv_buffer; 683275970Scy esrc = psrc + rbufp->recv_length; 684275970Scy 685275970Scy pdst = up->buffer + up->buflen; 686275970Scy edst = pdst + sizeof(up->buffer) - 1; /* for trailing NUL */ 687275970Scy 688275970Scy while (psrc != esrc) { 689275970Scy ch = *psrc++; 690275970Scy if (ch == '\n') { 691275970Scy /* trim trailing whitespace & terminate buffer */ 692275970Scy while (pdst != up->buffer && pdst[-1] <= ' ') 693275970Scy --pdst; 694275970Scy *pdst = '\0'; 695275970Scy /* process data and reset buffer */ 696290000Sglebius up->buflen = pdst - up->buffer; 697275970Scy gpsd_parse(peer, &rbufp->recv_time); 698275970Scy pdst = up->buffer; 699275970Scy } else if (pdst != edst) { 700275970Scy /* add next char, ignoring leading whitespace */ 701275970Scy if (ch > ' ' || pdst != up->buffer) 702275970Scy *pdst++ = ch; 703275970Scy } 704275970Scy } 705275970Scy up->buflen = pdst - up->buffer; 706275970Scy up->tickover = TICKOVER_LOW; 707275970Scy} 708275970Scy 709275970Scy/* ------------------------------------------------------------------ */ 710275970Scy 711275970Scystatic void 712290000Sglebiuspoll_primary( 713290000Sglebius peerT * const peer , 714290000Sglebius clockprocT * const pp , 715290000Sglebius gpsd_unitT * const up ) 716275970Scy{ 717275970Scy if (pp->coderecv != pp->codeproc) { 718275970Scy /* all is well */ 719275970Scy pp->lastref = pp->lastrec; 720290000Sglebius refclock_report(peer, CEVNT_NOMINAL); 721275970Scy refclock_receive(peer); 722275970Scy } else { 723290000Sglebius /* Not working properly, admit to it. If we have no 724290000Sglebius * connection to GPSD, declare the clock as faulty. If 725290000Sglebius * there were bad replies, this is handled as the major 726290000Sglebius * cause, and everything else is just a timeout. 727290000Sglebius */ 728275970Scy peer->precision = PRECISION; 729290000Sglebius if (-1 == pp->io.fd) 730275970Scy refclock_report(peer, CEVNT_FAULT); 731290000Sglebius else if (0 != up->tc_breply) 732275970Scy refclock_report(peer, CEVNT_BADREPLY); 733290000Sglebius else 734275970Scy refclock_report(peer, CEVNT_TIMEOUT); 735275970Scy } 736275970Scy 737275970Scy if (pp->sloppyclockflag & CLK_FLAG4) 738290000Sglebius mprintf_clock_stats( 739290000Sglebius &peer->srcadr,"%u %u %u %u %u %u %u", 740290000Sglebius up->tc_recv, 741290000Sglebius up->tc_breply, up->tc_nosync, 742290000Sglebius up->tc_sti_recv, up->tc_sti_used, 743290000Sglebius up->tc_pps_recv, up->tc_pps_used); 744275970Scy 745275970Scy /* clear tallies for next round */ 746290000Sglebius up->tc_breply = 0; 747290000Sglebius up->tc_recv = 0; 748290000Sglebius up->tc_nosync = 0; 749290000Sglebius up->tc_sti_recv = 0; 750290000Sglebius up->tc_sti_used = 0; 751290000Sglebius up->tc_pps_recv = 0; 752290000Sglebius up->tc_pps_used = 0; 753275970Scy} 754275970Scy 755290000Sglebiusstatic void 756290000Sglebiuspoll_secondary( 757290000Sglebius peerT * const peer , 758290000Sglebius clockprocT * const pp , 759290000Sglebius gpsd_unitT * const up ) 760290000Sglebius{ 761290000Sglebius if (pp->coderecv != pp->codeproc) { 762290000Sglebius /* all is well */ 763290000Sglebius pp->lastref = pp->lastrec; 764290000Sglebius refclock_report(peer, CEVNT_NOMINAL); 765290000Sglebius refclock_receive(peer); 766290000Sglebius } else { 767290000Sglebius peer->precision = PPS_PRECISION; 768290000Sglebius peer->flags &= ~FLAG_PPS; 769290000Sglebius refclock_report(peer, CEVNT_TIMEOUT); 770290000Sglebius } 771290000Sglebius} 772290000Sglebius 773290000Sglebiusstatic void 774290000Sglebiusgpsd_poll( 775290000Sglebius int unit, 776290000Sglebius peerT * peer) 777290000Sglebius{ 778290000Sglebius clockprocT * const pp = peer->procptr; 779290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 780290000Sglebius 781290000Sglebius ++pp->polls; 782290000Sglebius if (peer == up->pps_peer) 783290000Sglebius poll_secondary(peer, pp, up); 784290000Sglebius else 785290000Sglebius poll_primary(peer, pp, up); 786290000Sglebius} 787290000Sglebius 788275970Scy/* ------------------------------------------------------------------ */ 789275970Scy 790275970Scystatic void 791275970Scygpsd_control( 792275970Scy int unit, 793275970Scy const struct refclockstat * in_st, 794275970Scy struct refclockstat * out_st, 795275970Scy peerT * peer ) 796275970Scy{ 797275970Scy clockprocT * const pp = peer->procptr; 798275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 799275970Scy 800290000Sglebius if (peer == up->pps_peer) { 801290000Sglebius DTOLFP(pp->fudgetime1, &up->pps_fudge2); 802290000Sglebius if ( ! (pp->sloppyclockflag & CLK_FLAG1)) 803290000Sglebius peer->flags &= ~FLAG_PPS; 804290000Sglebius } else { 805290000Sglebius /* save preprocessed fudge times */ 806290000Sglebius DTOLFP(pp->fudgetime1, &up->pps_fudge); 807290000Sglebius DTOLFP(pp->fudgetime2, &up->sti_fudge); 808275970Scy 809290000Sglebius if (MODE_OP_MODE(up->mode ^ peer->ttl)) { 810290000Sglebius leave_opmode(peer, up->mode); 811290000Sglebius up->mode = MODE_OP_MODE(peer->ttl); 812290000Sglebius enter_opmode(peer, up->mode); 813290000Sglebius } 814290000Sglebius } 815290000Sglebius } 816290000Sglebius 817275970Scy/* ------------------------------------------------------------------ */ 818275970Scy 819275970Scystatic void 820290000Sglebiustimer_primary( 821290000Sglebius peerT * const peer , 822290000Sglebius clockprocT * const pp , 823290000Sglebius gpsd_unitT * const up ) 824275970Scy{ 825290000Sglebius int rc; 826275970Scy 827275970Scy /* This is used for timeout handling. Nothing that needs 828275970Scy * sub-second precison happens here, so receive/connect/retry 829275970Scy * timeouts are simply handled by a count down, and then we 830275970Scy * decide what to do by the socket values. 831275970Scy * 832275970Scy * Note that the timer stays at zero here, unless some of the 833275970Scy * functions set it to another value. 834275970Scy */ 835275970Scy if (up->logthrottle) 836275970Scy --up->logthrottle; 837275970Scy if (up->tickover) 838275970Scy --up->tickover; 839275970Scy switch (up->tickover) { 840275970Scy case 4: 841290000Sglebius /* If we are connected to GPSD, try to get a live signal 842290000Sglebius * by querying the version. Otherwise just check the 843290000Sglebius * socket to become ready. 844275970Scy */ 845275970Scy if (-1 != pp->io.fd) { 846290000Sglebius size_t rlen = strlen(s_req_version); 847290000Sglebius DPRINTF(2, ("%s: timer livecheck: '%s'\n", 848290000Sglebius up->logname, s_req_version)); 849290000Sglebius log_data(peer, "send", s_req_version, rlen); 850290000Sglebius rc = write(pp->io.fd, s_req_version, rlen); 851290000Sglebius (void)rc; 852275970Scy } else if (-1 != up->fdt) { 853275970Scy gpsd_test_socket(peer); 854275970Scy } 855275970Scy break; 856275970Scy 857275970Scy case 0: 858275970Scy if (-1 != pp->io.fd) 859275970Scy gpsd_stop_socket(peer); 860275970Scy else if (-1 != up->fdt) 861275970Scy gpsd_test_socket(peer); 862275970Scy else if (NULL != s_gpsd_addr) 863275970Scy gpsd_init_socket(peer); 864275970Scy break; 865275970Scy 866275970Scy default: 867275970Scy if (-1 == pp->io.fd && -1 != up->fdt) 868275970Scy gpsd_test_socket(peer); 869275970Scy } 870290000Sglebius} 871275970Scy 872290000Sglebiusstatic void 873290000Sglebiustimer_secondary( 874290000Sglebius peerT * const peer , 875290000Sglebius clockprocT * const pp , 876290000Sglebius gpsd_unitT * const up ) 877290000Sglebius{ 878290000Sglebius /* Reduce the count by one. Flush sample buffer and clear PPS 879290000Sglebius * flag when this happens. 880290000Sglebius */ 881290000Sglebius up->ppscount2 = max(0, (up->ppscount2 - 1)); 882290000Sglebius if (0 == up->ppscount2) { 883290000Sglebius if (pp->coderecv != pp->codeproc) { 884290000Sglebius refclock_report(peer, CEVNT_TIMEOUT); 885290000Sglebius pp->coderecv = pp->codeproc; 886290000Sglebius } 887275970Scy peer->flags &= ~FLAG_PPS; 888290000Sglebius } 889275970Scy} 890275970Scy 891290000Sglebiusstatic void 892290000Sglebiusgpsd_timer( 893290000Sglebius int unit, 894290000Sglebius peerT * peer) 895290000Sglebius{ 896290000Sglebius clockprocT * const pp = peer->procptr; 897290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 898290000Sglebius 899290000Sglebius if (peer == up->pps_peer) 900290000Sglebius timer_secondary(peer, pp, up); 901290000Sglebius else 902290000Sglebius timer_primary(peer, pp, up); 903290000Sglebius} 904290000Sglebius 905275970Scy/* ===================================================================== 906290000Sglebius * handle opmode switches 907290000Sglebius */ 908290000Sglebius 909290000Sglebiusstatic void 910290000Sglebiusenter_opmode( 911290000Sglebius peerT *peer, 912290000Sglebius int mode) 913290000Sglebius{ 914290000Sglebius clockprocT * const pp = peer->procptr; 915290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 916290000Sglebius 917290000Sglebius DPRINTF(1, ("%s: enter operation mode %d\n", 918290000Sglebius up->logname, MODE_OP_MODE(mode))); 919290000Sglebius 920290000Sglebius if (MODE_OP_MODE(mode) == MODE_OP_AUTO) { 921290000Sglebius up->fl_rawsti = 0; 922290000Sglebius up->ppscount = PPS_MAXCOUNT / 2; 923290000Sglebius } 924290000Sglebius up->fl_pps = 0; 925290000Sglebius up->fl_sti = 0; 926290000Sglebius} 927290000Sglebius 928290000Sglebius/* ------------------------------------------------------------------ */ 929290000Sglebius 930290000Sglebiusstatic void 931290000Sglebiusleave_opmode( 932290000Sglebius peerT *peer, 933290000Sglebius int mode) 934290000Sglebius{ 935290000Sglebius clockprocT * const pp = peer->procptr; 936290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 937290000Sglebius 938290000Sglebius DPRINTF(1, ("%s: leaving operation mode %d\n", 939290000Sglebius up->logname, MODE_OP_MODE(mode))); 940290000Sglebius 941290000Sglebius if (MODE_OP_MODE(mode) == MODE_OP_AUTO) { 942290000Sglebius up->fl_rawsti = 0; 943290000Sglebius up->ppscount = 0; 944290000Sglebius } 945290000Sglebius up->fl_pps = 0; 946290000Sglebius up->fl_sti = 0; 947290000Sglebius} 948290000Sglebius 949290000Sglebius/* ===================================================================== 950290000Sglebius * operation mode specific evaluation 951290000Sglebius */ 952290000Sglebius 953290000Sglebiusstatic void 954290000Sglebiusadd_clock_sample( 955290000Sglebius peerT * const peer , 956290000Sglebius clockprocT * const pp , 957290000Sglebius l_fp stamp, 958290000Sglebius l_fp recvt) 959290000Sglebius{ 960290000Sglebius pp->lastref = stamp; 961290000Sglebius if (pp->coderecv == pp->codeproc) 962290000Sglebius refclock_report(peer, CEVNT_NOMINAL); 963290000Sglebius refclock_process_offset(pp, stamp, recvt, 0.0); 964290000Sglebius} 965290000Sglebius 966290000Sglebius/* ------------------------------------------------------------------ */ 967290000Sglebius 968290000Sglebiusstatic void 969290000Sglebiuseval_strict( 970290000Sglebius peerT * const peer , 971290000Sglebius clockprocT * const pp , 972290000Sglebius gpsd_unitT * const up ) 973290000Sglebius{ 974290000Sglebius if (up->fl_sti && up->fl_pps) { 975290000Sglebius /* use TPV reference time + PPS receive time */ 976290000Sglebius add_clock_sample(peer, pp, up->sti_stamp, up->pps_recvt); 977290000Sglebius peer->precision = up->pps_prec; 978290000Sglebius /* both packets consumed now... */ 979290000Sglebius up->fl_pps = 0; 980290000Sglebius up->fl_sti = 0; 981290000Sglebius ++up->tc_sti_used; 982290000Sglebius } 983290000Sglebius} 984290000Sglebius 985290000Sglebius/* ------------------------------------------------------------------ */ 986290000Sglebius/* PPS processing for the secondary channel. GPSD provides us with full 987290000Sglebius * timing information, so there's no danger of PLL-locking to the wrong 988290000Sglebius * second. The belts and suspenders needed for the raw ATOM clock are 989290000Sglebius * unnecessary here. 990290000Sglebius */ 991290000Sglebiusstatic void 992290000Sglebiuseval_pps_secondary( 993290000Sglebius peerT * const peer , 994290000Sglebius clockprocT * const pp , 995290000Sglebius gpsd_unitT * const up ) 996290000Sglebius{ 997290000Sglebius if (up->fl_pps2) { 998290000Sglebius /* feed data */ 999290000Sglebius add_clock_sample(peer, pp, up->pps_stamp2, up->pps_recvt2); 1000290000Sglebius peer->precision = up->pps_prec; 1001290000Sglebius /* PPS peer flag logic */ 1002290000Sglebius up->ppscount2 = min(PPS2_MAXCOUNT, (up->ppscount2 + 2)); 1003290000Sglebius if ((PPS2_MAXCOUNT == up->ppscount2) && 1004290000Sglebius (pp->sloppyclockflag & CLK_FLAG1) ) 1005290000Sglebius peer->flags |= FLAG_PPS; 1006290000Sglebius /* mark time stamp as burned... */ 1007290000Sglebius up->fl_pps2 = 0; 1008290000Sglebius ++up->tc_pps_used; 1009290000Sglebius } 1010290000Sglebius} 1011290000Sglebius 1012290000Sglebius/* ------------------------------------------------------------------ */ 1013290000Sglebius 1014290000Sglebiusstatic void 1015290000Sglebiuseval_serial( 1016290000Sglebius peerT * const peer , 1017290000Sglebius clockprocT * const pp , 1018290000Sglebius gpsd_unitT * const up ) 1019290000Sglebius{ 1020290000Sglebius if (up->fl_sti) { 1021290000Sglebius add_clock_sample(peer, pp, up->sti_stamp, up->sti_recvt); 1022290000Sglebius peer->precision = up->sti_prec; 1023290000Sglebius /* mark time stamp as burned... */ 1024290000Sglebius up->fl_sti = 0; 1025290000Sglebius ++up->tc_sti_used; 1026290000Sglebius } 1027290000Sglebius} 1028290000Sglebius 1029290000Sglebius/* ------------------------------------------------------------------ */ 1030290000Sglebiusstatic void 1031290000Sglebiuseval_auto( 1032290000Sglebius peerT * const peer , 1033290000Sglebius clockprocT * const pp , 1034290000Sglebius gpsd_unitT * const up ) 1035290000Sglebius{ 1036290000Sglebius /* If there's no TPV available, stop working here... */ 1037290000Sglebius if (!up->fl_sti) 1038290000Sglebius return; 1039290000Sglebius 1040290000Sglebius /* check how to handle STI+PPS: Can PPS be used to augment STI 1041290000Sglebius * (or vice versae), do we drop the sample because there is a 1042290000Sglebius * temporary missing PPS signal, or do we feed on STI time 1043290000Sglebius * stamps alone? 1044290000Sglebius * 1045290000Sglebius * Do a counter/threshold dance to decide how to proceed. 1046290000Sglebius */ 1047290000Sglebius if (up->fl_pps) { 1048290000Sglebius up->ppscount = min(PPS_MAXCOUNT, 1049290000Sglebius (up->ppscount + PPS_INCCOUNT)); 1050290000Sglebius if ((PPS_MAXCOUNT == up->ppscount) && up->fl_rawsti) { 1051290000Sglebius up->fl_rawsti = 0; 1052290000Sglebius msyslog(LOG_INFO, 1053290000Sglebius "%s: expect valid PPS from now", 1054290000Sglebius up->logname); 1055290000Sglebius } 1056290000Sglebius } else { 1057290000Sglebius up->ppscount = max(0, (up->ppscount - PPS_DECCOUNT)); 1058290000Sglebius if ((0 == up->ppscount) && !up->fl_rawsti) { 1059290000Sglebius up->fl_rawsti = -1; 1060290000Sglebius msyslog(LOG_WARNING, 1061290000Sglebius "%s: use TPV alone from now", 1062290000Sglebius up->logname); 1063290000Sglebius } 1064290000Sglebius } 1065290000Sglebius 1066290000Sglebius /* now eventually feed the sample */ 1067290000Sglebius if (up->fl_rawsti) 1068290000Sglebius eval_serial(peer, pp, up); 1069290000Sglebius else 1070290000Sglebius eval_strict(peer, pp, up); 1071290000Sglebius} 1072290000Sglebius 1073290000Sglebius/* ===================================================================== 1074275970Scy * JSON parsing stuff 1075275970Scy */ 1076275970Scy 1077290000Sglebius/* ------------------------------------------------------------------ */ 1078290000Sglebius/* Parse a decimal integer with a possible sign. Works like 'strtoll()' 1079290000Sglebius * or 'strtol()', but with a fixed base of 10 and without eating away 1080290000Sglebius * leading whitespace. For the error codes, the handling of the end 1081290000Sglebius * pointer and the return values see 'strtol()'. 1082290000Sglebius */ 1083290000Sglebiusstatic json_int 1084290000Sglebiusstrtojint( 1085290000Sglebius const char *cp, char **ep) 1086290000Sglebius{ 1087290000Sglebius json_uint accu, limit_lo, limit_hi; 1088290000Sglebius int flags; /* bit 0: overflow; bit 1: sign */ 1089290000Sglebius const char * hold; 1090275970Scy 1091290000Sglebius /* pointer union to circumvent a tricky/sticky const issue */ 1092290000Sglebius union { const char * c; char * v; } vep; 1093275970Scy 1094290000Sglebius /* store initial value of 'cp' -- see 'strtol()' */ 1095290000Sglebius vep.c = cp; 1096275970Scy 1097290000Sglebius /* Eat away an optional sign and set the limits accordingly: The 1098290000Sglebius * high limit is the maximum absolute value that can be returned, 1099290000Sglebius * and the low limit is the biggest value that does not cause an 1100290000Sglebius * overflow when multiplied with 10. Avoid negation overflows. 1101290000Sglebius */ 1102290000Sglebius if (*cp == '-') { 1103290000Sglebius cp += 1; 1104290000Sglebius flags = 2; 1105290000Sglebius limit_hi = (json_uint)-(JSON_INT_MIN + 1) + 1; 1106290000Sglebius } else { 1107290000Sglebius cp += (*cp == '+'); 1108290000Sglebius flags = 0; 1109290000Sglebius limit_hi = (json_uint)JSON_INT_MAX; 1110290000Sglebius } 1111290000Sglebius limit_lo = limit_hi / 10; 1112275970Scy 1113290000Sglebius /* Now try to convert a sequence of digits. */ 1114290000Sglebius hold = cp; 1115290000Sglebius accu = 0; 1116290000Sglebius while (isdigit(*(const u_char*)cp)) { 1117290000Sglebius flags |= (accu > limit_lo); 1118290000Sglebius accu = accu * 10 + (*(const u_char*)cp++ - '0'); 1119290000Sglebius flags |= (accu > limit_hi); 1120290000Sglebius } 1121290000Sglebius /* Check for empty conversion (no digits seen). */ 1122290000Sglebius if (hold != cp) 1123290000Sglebius vep.c = cp; 1124290000Sglebius else 1125290000Sglebius errno = EINVAL; /* accu is still zero */ 1126290000Sglebius /* Check for range overflow */ 1127290000Sglebius if (flags & 1) { 1128290000Sglebius errno = ERANGE; 1129290000Sglebius accu = limit_hi; 1130290000Sglebius } 1131290000Sglebius /* If possible, store back the end-of-conversion pointer */ 1132290000Sglebius if (ep) 1133290000Sglebius *ep = vep.v; 1134290000Sglebius /* If negative, return the negated result if the accu is not 1135290000Sglebius * zero. Avoid negation overflows. 1136290000Sglebius */ 1137290000Sglebius if ((flags & 2) && accu) 1138290000Sglebius return -(json_int)(accu - 1) - 1; 1139290000Sglebius else 1140290000Sglebius return (json_int)accu; 1141290000Sglebius} 1142290000Sglebius 1143275970Scy/* ------------------------------------------------------------------ */ 1144275970Scy 1145275970Scystatic tok_ref 1146275970Scyjson_token_skip( 1147275970Scy const json_ctx * ctx, 1148275970Scy tok_ref tid) 1149275970Scy{ 1150290000Sglebius if (tid >= 0 && tid < ctx->ntok) { 1151290000Sglebius int len = ctx->tok[tid].size; 1152290000Sglebius /* For arrays and objects, the size is the number of 1153290000Sglebius * ITEMS in the compound. Thats the number of objects in 1154290000Sglebius * the array, and the number of key/value pairs for 1155290000Sglebius * objects. In theory, the key must be a string, and we 1156290000Sglebius * could simply skip one token before skipping the 1157290000Sglebius * value, which can be anything. We're a bit paranoid 1158290000Sglebius * and lazy at the same time: We simply double the 1159290000Sglebius * number of tokens to skip and fall through into the 1160290000Sglebius * array processing when encountering an object. 1161290000Sglebius */ 1162290000Sglebius switch (ctx->tok[tid].type) { 1163290000Sglebius case JSMN_OBJECT: 1164290000Sglebius len *= 2; 1165290000Sglebius /* FALLTHROUGH */ 1166290000Sglebius case JSMN_ARRAY: 1167290000Sglebius for (++tid; len; --len) 1168290000Sglebius tid = json_token_skip(ctx, tid); 1169275970Scy break; 1170290000Sglebius 1171290000Sglebius default: 1172290000Sglebius ++tid; 1173290000Sglebius break; 1174290000Sglebius } 1175290000Sglebius if (tid > ctx->ntok) /* Impossible? Paranoia rulez. */ 1176290000Sglebius tid = ctx->ntok; 1177290000Sglebius } 1178275970Scy return tid; 1179275970Scy} 1180290000Sglebius 1181275970Scy/* ------------------------------------------------------------------ */ 1182275970Scy 1183275970Scystatic int 1184275970Scyjson_object_lookup( 1185290000Sglebius const json_ctx * ctx , 1186290000Sglebius tok_ref tid , 1187290000Sglebius const char * key , 1188290000Sglebius int what) 1189275970Scy{ 1190275970Scy int len; 1191275970Scy 1192290000Sglebius if (tid < 0 || tid >= ctx->ntok || 1193290000Sglebius ctx->tok[tid].type != JSMN_OBJECT) 1194275970Scy return INVALID_TOKEN; 1195290000Sglebius 1196290000Sglebius len = ctx->tok[tid].size; 1197290000Sglebius for (++tid; len && tid+1 < ctx->ntok; --len) { 1198290000Sglebius if (ctx->tok[tid].type != JSMN_STRING) { /* Blooper! */ 1199290000Sglebius tid = json_token_skip(ctx, tid); /* skip key */ 1200290000Sglebius tid = json_token_skip(ctx, tid); /* skip val */ 1201290000Sglebius } else if (strcmp(key, ctx->buf + ctx->tok[tid].start)) { 1202290000Sglebius tid = json_token_skip(ctx, tid+1); /* skip key+val */ 1203290000Sglebius } else if (what < 0 || what == ctx->tok[tid+1].type) { 1204275970Scy return tid + 1; 1205290000Sglebius } else { 1206290000Sglebius break; 1207290000Sglebius } 1208290000Sglebius /* if skipping ahead returned an error, bail out here. */ 1209290000Sglebius if (tid < 0) 1210290000Sglebius break; 1211275970Scy } 1212275970Scy return INVALID_TOKEN; 1213275970Scy} 1214275970Scy 1215275970Scy/* ------------------------------------------------------------------ */ 1216275970Scy 1217275970Scystatic const char* 1218290000Sglebiusjson_object_lookup_primitive( 1219290000Sglebius const json_ctx * ctx, 1220290000Sglebius tok_ref tid, 1221290000Sglebius const char * key) 1222290000Sglebius{ 1223290000Sglebius tid = json_object_lookup(ctx, tid, key, JSMN_PRIMITIVE); 1224290000Sglebius if (INVALID_TOKEN != tid) 1225290000Sglebius return ctx->buf + ctx->tok[tid].start; 1226290000Sglebius else 1227290000Sglebius return NULL; 1228290000Sglebius} 1229290000Sglebius/* ------------------------------------------------------------------ */ 1230290000Sglebius/* look up a boolean value. This essentially returns a tribool: 1231290000Sglebius * 0->false, 1->true, (-1)->error/undefined 1232290000Sglebius */ 1233290000Sglebiusstatic int 1234290000Sglebiusjson_object_lookup_bool( 1235290000Sglebius const json_ctx * ctx, 1236290000Sglebius tok_ref tid, 1237290000Sglebius const char * key) 1238290000Sglebius{ 1239290000Sglebius const char *cp; 1240290000Sglebius cp = json_object_lookup_primitive(ctx, tid, key); 1241290000Sglebius switch ( cp ? *cp : '\0') { 1242290000Sglebius case 't': return 1; 1243290000Sglebius case 'f': return 0; 1244290000Sglebius default : return -1; 1245290000Sglebius } 1246290000Sglebius} 1247290000Sglebius 1248290000Sglebius/* ------------------------------------------------------------------ */ 1249290000Sglebius 1250290000Sglebiusstatic const char* 1251275970Scyjson_object_lookup_string( 1252275970Scy const json_ctx * ctx, 1253275970Scy tok_ref tid, 1254275970Scy const char * key) 1255275970Scy{ 1256290000Sglebius tid = json_object_lookup(ctx, tid, key, JSMN_STRING); 1257290000Sglebius if (INVALID_TOKEN != tid) 1258290000Sglebius return ctx->buf + ctx->tok[tid].start; 1259275970Scy return NULL; 1260275970Scy} 1261275970Scy 1262275970Scystatic const char* 1263275970Scyjson_object_lookup_string_default( 1264275970Scy const json_ctx * ctx, 1265275970Scy tok_ref tid, 1266275970Scy const char * key, 1267275970Scy const char * def) 1268275970Scy{ 1269290000Sglebius tid = json_object_lookup(ctx, tid, key, JSMN_STRING); 1270290000Sglebius if (INVALID_TOKEN != tid) 1271290000Sglebius return ctx->buf + ctx->tok[tid].start; 1272290000Sglebius return def; 1273275970Scy} 1274275970Scy 1275275970Scy/* ------------------------------------------------------------------ */ 1276275970Scy 1277275970Scystatic json_int 1278275970Scyjson_object_lookup_int( 1279275970Scy const json_ctx * ctx, 1280275970Scy tok_ref tid, 1281275970Scy const char * key) 1282275970Scy{ 1283290000Sglebius json_int ret; 1284290000Sglebius const char * cp; 1285290000Sglebius char * ep; 1286275970Scy 1287290000Sglebius cp = json_object_lookup_primitive(ctx, tid, key); 1288290000Sglebius if (NULL != cp) { 1289290000Sglebius ret = strtojint(cp, &ep); 1290290000Sglebius if (cp != ep && '\0' == *ep) 1291290000Sglebius return ret; 1292290000Sglebius } else { 1293290000Sglebius errno = EINVAL; 1294290000Sglebius } 1295275970Scy return 0; 1296275970Scy} 1297275970Scy 1298275970Scystatic json_int 1299275970Scyjson_object_lookup_int_default( 1300275970Scy const json_ctx * ctx, 1301275970Scy tok_ref tid, 1302275970Scy const char * key, 1303275970Scy json_int def) 1304275970Scy{ 1305290000Sglebius json_int ret; 1306290000Sglebius const char * cp; 1307290000Sglebius char * ep; 1308290000Sglebius 1309290000Sglebius cp = json_object_lookup_primitive(ctx, tid, key); 1310290000Sglebius if (NULL != cp) { 1311290000Sglebius ret = strtojint(cp, &ep); 1312290000Sglebius if (cp != ep && '\0' == *ep) 1313290000Sglebius return ret; 1314290000Sglebius } 1315290000Sglebius return def; 1316275970Scy} 1317275970Scy 1318275970Scy/* ------------------------------------------------------------------ */ 1319290000Sglebius#if 0 /* currently unused */ 1320275970Scystatic double 1321275970Scyjson_object_lookup_float( 1322275970Scy const json_ctx * ctx, 1323275970Scy tok_ref tid, 1324275970Scy const char * key) 1325275970Scy{ 1326290000Sglebius double ret; 1327290000Sglebius const char * cp; 1328290000Sglebius char * ep; 1329275970Scy 1330290000Sglebius cp = json_object_lookup_primitive(ctx, tid, key); 1331290000Sglebius if (NULL != cp) { 1332290000Sglebius ret = strtod(cp, &ep); 1333290000Sglebius if (cp != ep && '\0' == *ep) 1334290000Sglebius return ret; 1335290000Sglebius } else { 1336290000Sglebius errno = EINVAL; 1337290000Sglebius } 1338275970Scy return 0.0; 1339275970Scy} 1340290000Sglebius#endif 1341275970Scy 1342275970Scystatic double 1343275970Scyjson_object_lookup_float_default( 1344275970Scy const json_ctx * ctx, 1345275970Scy tok_ref tid, 1346275970Scy const char * key, 1347275970Scy double def) 1348275970Scy{ 1349290000Sglebius double ret; 1350290000Sglebius const char * cp; 1351290000Sglebius char * ep; 1352290000Sglebius 1353290000Sglebius cp = json_object_lookup_primitive(ctx, tid, key); 1354290000Sglebius if (NULL != cp) { 1355290000Sglebius ret = strtod(cp, &ep); 1356290000Sglebius if (cp != ep && '\0' == *ep) 1357290000Sglebius return ret; 1358290000Sglebius } 1359290000Sglebius return def; 1360275970Scy} 1361275970Scy 1362275970Scy/* ------------------------------------------------------------------ */ 1363275970Scy 1364275970Scystatic BOOL 1365275970Scyjson_parse_record( 1366275970Scy json_ctx * ctx, 1367290000Sglebius char * buf, 1368290000Sglebius size_t len) 1369275970Scy{ 1370275970Scy jsmn_parser jsm; 1371275970Scy int idx, rc; 1372275970Scy 1373275970Scy jsmn_init(&jsm); 1374290000Sglebius rc = jsmn_parse(&jsm, buf, len, ctx->tok, JSMN_MAXTOK); 1375290000Sglebius if (rc <= 0) 1376290000Sglebius return FALSE; 1377275970Scy ctx->buf = buf; 1378290000Sglebius ctx->ntok = rc; 1379275970Scy 1380290000Sglebius if (JSMN_OBJECT != ctx->tok[0].type) 1381290000Sglebius return FALSE; /* not object!?! */ 1382290000Sglebius 1383275970Scy /* Make all tokens NUL terminated by overwriting the 1384290000Sglebius * terminator symbol. Makes string compares and number parsing a 1385290000Sglebius * lot easier! 1386275970Scy */ 1387290000Sglebius for (idx = 0; idx < ctx->ntok; ++idx) 1388275970Scy if (ctx->tok[idx].end > ctx->tok[idx].start) 1389275970Scy ctx->buf[ctx->tok[idx].end] = '\0'; 1390275970Scy return TRUE; 1391275970Scy} 1392275970Scy 1393275970Scy 1394275970Scy/* ===================================================================== 1395275970Scy * static local helpers 1396275970Scy */ 1397290000Sglebiusstatic BOOL 1398290000Sglebiusget_binary_time( 1399290000Sglebius l_fp * const dest , 1400290000Sglebius json_ctx * const jctx , 1401290000Sglebius const char * const time_name, 1402290000Sglebius const char * const frac_name, 1403290000Sglebius long fscale ) 1404290000Sglebius{ 1405290000Sglebius BOOL retv = FALSE; 1406290000Sglebius struct timespec ts; 1407275970Scy 1408290000Sglebius errno = 0; 1409290000Sglebius ts.tv_sec = (time_t)json_object_lookup_int(jctx, 0, time_name); 1410290000Sglebius ts.tv_nsec = (long )json_object_lookup_int(jctx, 0, frac_name); 1411290000Sglebius if (0 == errno) { 1412290000Sglebius ts.tv_nsec *= fscale; 1413290000Sglebius *dest = tspec_stamp_to_lfp(ts); 1414290000Sglebius retv = TRUE; 1415290000Sglebius } 1416290000Sglebius return retv; 1417290000Sglebius} 1418290000Sglebius 1419275970Scy/* ------------------------------------------------------------------ */ 1420275970Scy/* Process a WATCH record 1421275970Scy * 1422275970Scy * Currently this is only used to recognise that the device is present 1423275970Scy * and that we're listed subscribers. 1424275970Scy */ 1425275970Scystatic void 1426275970Scyprocess_watch( 1427275970Scy peerT * const peer , 1428275970Scy json_ctx * const jctx , 1429275970Scy const l_fp * const rtime) 1430275970Scy{ 1431275970Scy clockprocT * const pp = peer->procptr; 1432275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1433275970Scy 1434290000Sglebius const char * path; 1435290000Sglebius 1436290000Sglebius path = json_object_lookup_string(jctx, 0, "device"); 1437290000Sglebius if (NULL == path || strcmp(path, up->device)) 1438290000Sglebius return; 1439290000Sglebius 1440290000Sglebius if (json_object_lookup_bool(jctx, 0, "enable") > 0 && 1441290000Sglebius json_object_lookup_bool(jctx, 0, "json" ) > 0 ) 1442290000Sglebius up->fl_watch = -1; 1443290000Sglebius else 1444290000Sglebius up->fl_watch = 0; 1445290000Sglebius DPRINTF(2, ("%s: process_watch, enabled=%d\n", 1446290000Sglebius up->logname, (up->fl_watch & 1))); 1447275970Scy} 1448275970Scy 1449275970Scy/* ------------------------------------------------------------------ */ 1450275970Scy 1451275970Scystatic void 1452275970Scyprocess_version( 1453275970Scy peerT * const peer , 1454275970Scy json_ctx * const jctx , 1455275970Scy const l_fp * const rtime) 1456275970Scy{ 1457275970Scy clockprocT * const pp = peer->procptr; 1458275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1459275970Scy 1460275970Scy int len; 1461275970Scy char * buf; 1462275970Scy const char *revision; 1463275970Scy const char *release; 1464290000Sglebius uint16_t pvhi, pvlo; 1465275970Scy 1466275970Scy /* get protocol version number */ 1467275970Scy revision = json_object_lookup_string_default( 1468290000Sglebius jctx, 0, "rev", "(unknown)"); 1469275970Scy release = json_object_lookup_string_default( 1470290000Sglebius jctx, 0, "release", "(unknown)"); 1471275970Scy errno = 0; 1472290000Sglebius pvhi = (uint16_t)json_object_lookup_int(jctx, 0, "proto_major"); 1473290000Sglebius pvlo = (uint16_t)json_object_lookup_int(jctx, 0, "proto_minor"); 1474290000Sglebius 1475275970Scy if (0 == errno) { 1476290000Sglebius if ( ! up->fl_vers) 1477290000Sglebius msyslog(LOG_INFO, 1478290000Sglebius "%s: GPSD revision=%s release=%s protocol=%u.%u", 1479290000Sglebius up->logname, revision, release, 1480290000Sglebius pvhi, pvlo); 1481290000Sglebius up->proto_version = PROTO_VERSION(pvhi, pvlo); 1482275970Scy up->fl_vers = -1; 1483290000Sglebius } else { 1484275970Scy if (syslogok(pp, up)) 1485275970Scy msyslog(LOG_INFO, 1486290000Sglebius "%s: could not evaluate version data", 1487290000Sglebius up->logname); 1488290000Sglebius return; 1489275970Scy } 1490290000Sglebius /* With the 3.9 GPSD protocol, '*_musec' vanished from the PPS 1491290000Sglebius * record and was replace by '*_nsec'. 1492290000Sglebius */ 1493290000Sglebius up->pf_nsec = -(up->proto_version >= PROTO_VERSION(3,9)); 1494275970Scy 1495290000Sglebius /* With the 3.10 protocol we can get TOFF records for better 1496290000Sglebius * timing information. 1497275970Scy */ 1498290000Sglebius up->pf_toff = -(up->proto_version >= PROTO_VERSION(3,10)); 1499275970Scy 1500290000Sglebius /* request watch for our GPS device if not yet watched. 1501290000Sglebius * 1502290000Sglebius * The version string is also sent as a life signal, if we have 1503290000Sglebius * seen useable data. So if we're already watching the device, 1504290000Sglebius * skip the request. 1505290000Sglebius * 1506275970Scy * Reuse the input buffer, which is no longer needed in the 1507275970Scy * current cycle. Also assume that we can write the watch 1508275970Scy * request in one sweep into the socket; since we do not do 1509275970Scy * output otherwise, this should always work. (Unless the 1510275970Scy * TCP/IP window size gets lower than the length of the 1511275970Scy * request. We handle that when it happens.) 1512275970Scy */ 1513290000Sglebius if (up->fl_watch) 1514290000Sglebius return; 1515290000Sglebius 1516275970Scy snprintf(up->buffer, sizeof(up->buffer), 1517290000Sglebius s_req_watch[up->pf_toff != 0], up->device); 1518275970Scy buf = up->buffer; 1519275970Scy len = strlen(buf); 1520290000Sglebius log_data(peer, "send", buf, len); 1521290000Sglebius if (len != write(pp->io.fd, buf, len) && (syslogok(pp, up))) { 1522290000Sglebius /* Note: if the server fails to read our request, the 1523275970Scy * resulting data timeout will take care of the 1524275970Scy * connection! 1525275970Scy */ 1526290000Sglebius msyslog(LOG_ERR, "%s: failed to write watch request (%m)", 1527290000Sglebius up->logname); 1528275970Scy } 1529275970Scy} 1530275970Scy 1531275970Scy/* ------------------------------------------------------------------ */ 1532275970Scy 1533275970Scystatic void 1534275970Scyprocess_tpv( 1535275970Scy peerT * const peer , 1536275970Scy json_ctx * const jctx , 1537275970Scy const l_fp * const rtime) 1538275970Scy{ 1539275970Scy clockprocT * const pp = peer->procptr; 1540275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1541275970Scy 1542275970Scy const char * gps_time; 1543275970Scy int gps_mode; 1544290000Sglebius double ept; 1545280849Scy int xlog2; 1546275970Scy 1547275970Scy gps_mode = (int)json_object_lookup_int_default( 1548275970Scy jctx, 0, "mode", 0); 1549275970Scy 1550290000Sglebius gps_time = json_object_lookup_string( 1551290000Sglebius jctx, 0, "time"); 1552275970Scy 1553290000Sglebius /* accept time stamps only in 2d or 3d fix */ 1554290000Sglebius if (gps_mode < 2 || NULL == gps_time) { 1555275970Scy /* receiver has no fix; tell about and avoid stale data */ 1556290000Sglebius if ( ! up->pf_toff) 1557290000Sglebius ++up->tc_sti_recv; 1558290000Sglebius ++up->tc_nosync; 1559290000Sglebius up->fl_sti = 0; 1560290000Sglebius up->fl_pps = 0; 1561290000Sglebius up->fl_nosync = -1; 1562275970Scy return; 1563275970Scy } 1564290000Sglebius up->fl_nosync = 0; 1565275970Scy 1566290000Sglebius /* convert clock and set resulting ref time, but only if the 1567290000Sglebius * TOFF sentence is *not* available 1568290000Sglebius */ 1569290000Sglebius if ( ! up->pf_toff) { 1570290000Sglebius ++up->tc_sti_recv; 1571290000Sglebius /* save last time code to clock data */ 1572290000Sglebius save_ltc(pp, gps_time); 1573290000Sglebius /* now parse the time string */ 1574290000Sglebius if (convert_ascii_time(&up->sti_stamp, gps_time)) { 1575290000Sglebius DPRINTF(2, ("%s: process_tpv, stamp='%s'," 1576290000Sglebius " recvt='%s' mode=%u\n", 1577290000Sglebius up->logname, 1578290000Sglebius gmprettydate(&up->sti_stamp), 1579290000Sglebius gmprettydate(&up->sti_recvt), 1580290000Sglebius gps_mode)); 1581275970Scy 1582290000Sglebius /* have to use local receive time as substitute 1583290000Sglebius * for the real receive time: TPV does not tell 1584290000Sglebius * us. 1585290000Sglebius */ 1586290000Sglebius up->sti_local = *rtime; 1587290000Sglebius up->sti_recvt = *rtime; 1588290000Sglebius L_SUB(&up->sti_recvt, &up->sti_fudge); 1589290000Sglebius up->fl_sti = -1; 1590290000Sglebius } else { 1591290000Sglebius ++up->tc_breply; 1592290000Sglebius up->fl_sti = 0; 1593290000Sglebius } 1594275970Scy } 1595290000Sglebius 1596275970Scy /* Set the precision from the GPSD data 1597290000Sglebius * Use the ETP field for an estimation of the precision of the 1598290000Sglebius * serial data. If ETP is not available, use the default serial 1599290000Sglebius * data presion instead. (Note: The PPS branch has a different 1600290000Sglebius * precision estimation, since it gets the proper value directly 1601290000Sglebius * from GPSD!) 1602275970Scy */ 1603290000Sglebius ept = json_object_lookup_float_default(jctx, 0, "ept", 2.0e-3); 1604290000Sglebius ept = frexp(fabs(ept)*0.70710678, &xlog2); /* ~ sqrt(0.5) */ 1605290000Sglebius if (ept < 0.25) 1606290000Sglebius xlog2 = INT_MIN; 1607290000Sglebius if (ept > 2.0) 1608290000Sglebius xlog2 = INT_MAX; 1609290000Sglebius up->sti_prec = clamped_precision(xlog2); 1610275970Scy} 1611275970Scy 1612275970Scy/* ------------------------------------------------------------------ */ 1613275970Scy 1614275970Scystatic void 1615275970Scyprocess_pps( 1616275970Scy peerT * const peer , 1617275970Scy json_ctx * const jctx , 1618275970Scy const l_fp * const rtime) 1619275970Scy{ 1620275970Scy clockprocT * const pp = peer->procptr; 1621275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1622275970Scy 1623290000Sglebius int xlog2; 1624275970Scy 1625290000Sglebius ++up->tc_pps_recv; 1626275970Scy 1627290000Sglebius /* Bail out if there's indication that time sync is bad or 1628290000Sglebius * if we're explicitely requested to ignore PPS data. 1629290000Sglebius */ 1630290000Sglebius if (up->fl_nosync) 1631290000Sglebius return; 1632290000Sglebius 1633275970Scy up->pps_local = *rtime; 1634290000Sglebius /* Now grab the time values. 'clock_*' is the event time of the 1635290000Sglebius * pulse measured on the local system clock; 'real_*' is the GPS 1636290000Sglebius * reference time GPSD associated with the pulse. 1637290000Sglebius */ 1638290000Sglebius if (up->pf_nsec) { 1639290000Sglebius if ( ! get_binary_time(&up->pps_recvt2, jctx, 1640290000Sglebius "clock_sec", "clock_nsec", 1)) 1641290000Sglebius goto fail; 1642290000Sglebius if ( ! get_binary_time(&up->pps_stamp2, jctx, 1643290000Sglebius "real_sec", "real_nsec", 1)) 1644290000Sglebius goto fail; 1645290000Sglebius } else { 1646290000Sglebius if ( ! get_binary_time(&up->pps_recvt2, jctx, 1647290000Sglebius "clock_sec", "clock_musec", 1000)) 1648290000Sglebius goto fail; 1649290000Sglebius if ( ! get_binary_time(&up->pps_stamp2, jctx, 1650290000Sglebius "real_sec", "real_musec", 1000)) 1651290000Sglebius goto fail; 1652290000Sglebius } 1653275970Scy 1654290000Sglebius /* Try to read the precision field from the PPS record. If it's 1655290000Sglebius * not there, take the precision from the serial data. 1656290000Sglebius */ 1657290000Sglebius xlog2 = json_object_lookup_int_default( 1658290000Sglebius jctx, 0, "precision", up->sti_prec); 1659290000Sglebius up->pps_prec = clamped_precision(xlog2); 1660290000Sglebius 1661290000Sglebius /* Get fudged receive times for primary & secondary unit */ 1662290000Sglebius up->pps_recvt = up->pps_recvt2; 1663290000Sglebius L_SUB(&up->pps_recvt , &up->pps_fudge ); 1664290000Sglebius L_SUB(&up->pps_recvt2, &up->pps_fudge2); 1665290000Sglebius pp->lastrec = up->pps_recvt; 1666290000Sglebius 1667290000Sglebius /* Map to nearest full second as reference time stamp for the 1668290000Sglebius * primary channel. Sanity checks are done in evaluation step. 1669290000Sglebius */ 1670275970Scy up->pps_stamp = up->pps_recvt; 1671275970Scy L_ADDUF(&up->pps_stamp, 0x80000000u); 1672275970Scy up->pps_stamp.l_uf = 0; 1673275970Scy 1674290000Sglebius if (NULL != up->pps_peer) 1675290000Sglebius save_ltc(up->pps_peer->procptr, 1676290000Sglebius gmprettydate(&up->pps_stamp2)); 1677290000Sglebius DPRINTF(2, ("%s: PPS record processed," 1678290000Sglebius " stamp='%s', recvt='%s'\n", 1679290000Sglebius up->logname, 1680290000Sglebius gmprettydate(&up->pps_stamp2), 1681290000Sglebius gmprettydate(&up->pps_recvt2))); 1682275970Scy 1683290000Sglebius up->fl_pps = (0 != (pp->sloppyclockflag & CLK_FLAG2)) - 1; 1684290000Sglebius up->fl_pps2 = -1; 1685275970Scy return; 1686275970Scy 1687275970Scy fail: 1688290000Sglebius DPRINTF(1, ("%s: PPS record processing FAILED\n", 1689290000Sglebius up->logname)); 1690290000Sglebius ++up->tc_breply; 1691275970Scy} 1692275970Scy 1693275970Scy/* ------------------------------------------------------------------ */ 1694275970Scy 1695275970Scystatic void 1696290000Sglebiusprocess_toff( 1697290000Sglebius peerT * const peer , 1698290000Sglebius json_ctx * const jctx , 1699290000Sglebius const l_fp * const rtime) 1700290000Sglebius{ 1701290000Sglebius clockprocT * const pp = peer->procptr; 1702290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1703290000Sglebius 1704290000Sglebius ++up->tc_sti_recv; 1705290000Sglebius 1706290000Sglebius /* remember this! */ 1707290000Sglebius up->pf_toff = -1; 1708290000Sglebius 1709290000Sglebius /* bail out if there's indication that time sync is bad */ 1710290000Sglebius if (up->fl_nosync) 1711290000Sglebius return; 1712290000Sglebius 1713290000Sglebius if ( ! get_binary_time(&up->sti_recvt, jctx, 1714290000Sglebius "clock_sec", "clock_nsec", 1)) 1715290000Sglebius goto fail; 1716290000Sglebius if ( ! get_binary_time(&up->sti_stamp, jctx, 1717290000Sglebius "real_sec", "real_nsec", 1)) 1718290000Sglebius goto fail; 1719290000Sglebius L_SUB(&up->sti_recvt, &up->sti_fudge); 1720290000Sglebius up->sti_local = *rtime; 1721290000Sglebius up->fl_sti = -1; 1722290000Sglebius 1723290000Sglebius save_ltc(pp, gmprettydate(&up->sti_stamp)); 1724290000Sglebius DPRINTF(2, ("%s: TOFF record processed," 1725290000Sglebius " stamp='%s', recvt='%s'\n", 1726290000Sglebius up->logname, 1727290000Sglebius gmprettydate(&up->sti_stamp), 1728290000Sglebius gmprettydate(&up->sti_recvt))); 1729290000Sglebius return; 1730290000Sglebius 1731290000Sglebius fail: 1732290000Sglebius DPRINTF(1, ("%s: TOFF record processing FAILED\n", 1733290000Sglebius up->logname)); 1734290000Sglebius ++up->tc_breply; 1735290000Sglebius} 1736290000Sglebius 1737290000Sglebius/* ------------------------------------------------------------------ */ 1738290000Sglebius 1739290000Sglebiusstatic void 1740275970Scygpsd_parse( 1741275970Scy peerT * const peer , 1742275970Scy const l_fp * const rtime) 1743275970Scy{ 1744275970Scy clockprocT * const pp = peer->procptr; 1745275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1746275970Scy 1747275970Scy const char * clsid; 1748275970Scy 1749290000Sglebius DPRINTF(2, ("%s: gpsd_parse: time %s '%.*s'\n", 1750290000Sglebius up->logname, ulfptoa(rtime, 6), 1751290000Sglebius up->buflen, up->buffer)); 1752275970Scy 1753290000Sglebius /* See if we can grab anything potentially useful. JSMN does not 1754290000Sglebius * need a trailing NUL, but it needs the number of bytes to 1755290000Sglebius * process. */ 1756290000Sglebius if (!json_parse_record(&up->json_parse, up->buffer, up->buflen)) { 1757290000Sglebius ++up->tc_breply; 1758275970Scy return; 1759290000Sglebius } 1760290000Sglebius 1761275970Scy /* Now dispatch over the objects we know */ 1762290000Sglebius clsid = json_object_lookup_string(&up->json_parse, 0, "class"); 1763290000Sglebius if (NULL == clsid) { 1764290000Sglebius ++up->tc_breply; 1765290000Sglebius return; 1766290000Sglebius } 1767275970Scy 1768290000Sglebius if (!strcmp("TPV", clsid)) 1769290000Sglebius process_tpv(peer, &up->json_parse, rtime); 1770275970Scy else if (!strcmp("PPS", clsid)) 1771290000Sglebius process_pps(peer, &up->json_parse, rtime); 1772290000Sglebius else if (!strcmp("TOFF", clsid)) 1773290000Sglebius process_toff(peer, &up->json_parse, rtime); 1774290000Sglebius else if (!strcmp("VERSION", clsid)) 1775290000Sglebius process_version(peer, &up->json_parse, rtime); 1776275970Scy else if (!strcmp("WATCH", clsid)) 1777290000Sglebius process_watch(peer, &up->json_parse, rtime); 1778275970Scy else 1779275970Scy return; /* nothing we know about... */ 1780290000Sglebius ++up->tc_recv; 1781275970Scy 1782290000Sglebius /* if possible, feed the PPS side channel */ 1783290000Sglebius if (up->pps_peer) 1784290000Sglebius eval_pps_secondary( 1785290000Sglebius up->pps_peer, up->pps_peer->procptr, up); 1786275970Scy 1787290000Sglebius /* check PPS vs. STI receive times: 1788290000Sglebius * If STI is before PPS, then clearly the STI is too old. If PPS 1789290000Sglebius * is before STI by more than one second, then PPS is too old. 1790290000Sglebius * Weed out stale time stamps & flags. 1791290000Sglebius */ 1792290000Sglebius if (up->fl_pps && up->fl_sti) { 1793290000Sglebius l_fp diff; 1794290000Sglebius diff = up->sti_local; 1795290000Sglebius L_SUB(&diff, &up->pps_local); 1796290000Sglebius if (diff.l_i > 0) 1797290000Sglebius up->fl_pps = 0; /* pps too old */ 1798290000Sglebius else if (diff.l_i < 0) 1799290000Sglebius up->fl_sti = 0; /* serial data too old */ 1800275970Scy } 1801290000Sglebius 1802290000Sglebius /* dispatch to the mode-dependent processing functions */ 1803290000Sglebius switch (up->mode) { 1804290000Sglebius default: 1805290000Sglebius case MODE_OP_STI: 1806290000Sglebius eval_serial(peer, pp, up); 1807290000Sglebius break; 1808290000Sglebius 1809290000Sglebius case MODE_OP_STRICT: 1810290000Sglebius eval_strict(peer, pp, up); 1811290000Sglebius break; 1812290000Sglebius 1813290000Sglebius case MODE_OP_AUTO: 1814290000Sglebius eval_auto(peer, pp, up); 1815290000Sglebius break; 1816290000Sglebius } 1817275970Scy} 1818275970Scy 1819275970Scy/* ------------------------------------------------------------------ */ 1820275970Scy 1821275970Scystatic void 1822275970Scygpsd_stop_socket( 1823275970Scy peerT * const peer) 1824275970Scy{ 1825275970Scy clockprocT * const pp = peer->procptr; 1826275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1827275970Scy 1828290000Sglebius if (-1 != pp->io.fd) { 1829290000Sglebius if (syslogok(pp, up)) 1830290000Sglebius msyslog(LOG_INFO, 1831290000Sglebius "%s: closing socket to GPSD, fd=%d", 1832290000Sglebius up->logname, pp->io.fd); 1833290000Sglebius else 1834290000Sglebius DPRINTF(1, ("%s: closing socket to GPSD, fd=%d\n", 1835290000Sglebius up->logname, pp->io.fd)); 1836275970Scy io_closeclock(&pp->io); 1837290000Sglebius pp->io.fd = -1; 1838290000Sglebius } 1839275970Scy up->tickover = up->tickpres; 1840275970Scy up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH); 1841275970Scy up->fl_vers = 0; 1842290000Sglebius up->fl_sti = 0; 1843275970Scy up->fl_pps = 0; 1844275970Scy up->fl_watch = 0; 1845275970Scy} 1846275970Scy 1847275970Scy/* ------------------------------------------------------------------ */ 1848275970Scy 1849275970Scystatic void 1850275970Scygpsd_init_socket( 1851275970Scy peerT * const peer) 1852275970Scy{ 1853275970Scy clockprocT * const pp = peer->procptr; 1854275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1855275970Scy addrinfoT * ai; 1856275970Scy int rc; 1857275970Scy int ov; 1858275970Scy 1859275970Scy /* draw next address to try */ 1860275970Scy if (NULL == up->addr) 1861275970Scy up->addr = s_gpsd_addr; 1862275970Scy ai = up->addr; 1863275970Scy up->addr = ai->ai_next; 1864275970Scy 1865275970Scy /* try to create a matching socket */ 1866275970Scy up->fdt = socket( 1867275970Scy ai->ai_family, ai->ai_socktype, ai->ai_protocol); 1868275970Scy if (-1 == up->fdt) { 1869275970Scy if (syslogok(pp, up)) 1870275970Scy msyslog(LOG_ERR, 1871275970Scy "%s: cannot create GPSD socket: %m", 1872290000Sglebius up->logname); 1873275970Scy goto no_socket; 1874275970Scy } 1875290000Sglebius 1876290000Sglebius /* Make sure the socket is non-blocking. Connect/reconnect and 1877290000Sglebius * IO happen in an event-driven environment, and synchronous 1878290000Sglebius * operations wreak havoc on that. 1879290000Sglebius */ 1880275970Scy rc = fcntl(up->fdt, F_SETFL, O_NONBLOCK, 1); 1881275970Scy if (-1 == rc) { 1882275970Scy if (syslogok(pp, up)) 1883275970Scy msyslog(LOG_ERR, 1884275970Scy "%s: cannot set GPSD socket to non-blocking: %m", 1885290000Sglebius up->logname); 1886275970Scy goto no_socket; 1887275970Scy } 1888290000Sglebius /* Disable nagling. The way both GPSD and NTPD handle the 1889290000Sglebius * protocol makes it record-oriented, and in most cases 1890290000Sglebius * complete records (JSON serialised objects) will be sent in 1891290000Sglebius * one sweep. Nagling gives not much advantage but adds another 1892290000Sglebius * delay, which can worsen the situation for some packets. 1893290000Sglebius */ 1894275970Scy ov = 1; 1895275970Scy rc = setsockopt(up->fdt, IPPROTO_TCP, TCP_NODELAY, 1896275970Scy (char*)&ov, sizeof(ov)); 1897275970Scy if (-1 == rc) { 1898275970Scy if (syslogok(pp, up)) 1899275970Scy msyslog(LOG_INFO, 1900275970Scy "%s: cannot disable TCP nagle: %m", 1901290000Sglebius up->logname); 1902275970Scy } 1903275970Scy 1904290000Sglebius /* Start a non-blocking connect. There might be a synchronous 1905290000Sglebius * connection result we have to handle. 1906290000Sglebius */ 1907275970Scy rc = connect(up->fdt, ai->ai_addr, ai->ai_addrlen); 1908290000Sglebius if (-1 == rc) { 1909290000Sglebius if (errno == EINPROGRESS) { 1910290000Sglebius DPRINTF(1, ("%s: async connect pending, fd=%d\n", 1911290000Sglebius up->logname, up->fdt)); 1912290000Sglebius return; 1913290000Sglebius } 1914290000Sglebius 1915275970Scy if (syslogok(pp, up)) 1916275970Scy msyslog(LOG_ERR, 1917275970Scy "%s: cannot connect GPSD socket: %m", 1918290000Sglebius up->logname); 1919275970Scy goto no_socket; 1920275970Scy } 1921275970Scy 1922290000Sglebius /* We had a successful synchronous connect, so we add the 1923290000Sglebius * refclock processing ASAP. We still have to wait for the 1924290000Sglebius * version string and apply the watch command later on, but we 1925290000Sglebius * might as well get the show on the road now. 1926290000Sglebius */ 1927290000Sglebius DPRINTF(1, ("%s: new socket connection, fd=%d\n", 1928290000Sglebius up->logname, up->fdt)); 1929290000Sglebius 1930290000Sglebius pp->io.fd = up->fdt; 1931290000Sglebius up->fdt = -1; 1932290000Sglebius if (0 == io_addclock(&pp->io)) { 1933290000Sglebius if (syslogok(pp, up)) 1934290000Sglebius msyslog(LOG_ERR, 1935290000Sglebius "%s: failed to register with I/O engine", 1936290000Sglebius up->logname); 1937290000Sglebius goto no_socket; 1938290000Sglebius } 1939290000Sglebius 1940275970Scy return; 1941290000Sglebius 1942275970Scy no_socket: 1943290000Sglebius if (-1 != pp->io.fd) 1944290000Sglebius close(pp->io.fd); 1945275970Scy if (-1 != up->fdt) 1946275970Scy close(up->fdt); 1947290000Sglebius pp->io.fd = -1; 1948275970Scy up->fdt = -1; 1949275970Scy up->tickover = up->tickpres; 1950275970Scy up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH); 1951275970Scy} 1952275970Scy 1953275970Scy/* ------------------------------------------------------------------ */ 1954275970Scy 1955275970Scystatic void 1956275970Scygpsd_test_socket( 1957275970Scy peerT * const peer) 1958275970Scy{ 1959275970Scy clockprocT * const pp = peer->procptr; 1960275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1961275970Scy 1962275970Scy int ec, rc; 1963275970Scy socklen_t lc; 1964275970Scy 1965275970Scy /* Check if the non-blocking connect was finished by testing the 1966275970Scy * socket for writeability. Use the 'poll()' API if available 1967275970Scy * and 'select()' otherwise. 1968275970Scy */ 1969290000Sglebius DPRINTF(2, ("%s: check connect, fd=%d\n", 1970290000Sglebius up->logname, up->fdt)); 1971275970Scy 1972275970Scy#if defined(HAVE_SYS_POLL_H) 1973275970Scy { 1974275970Scy struct pollfd pfd; 1975275970Scy 1976275970Scy pfd.events = POLLOUT; 1977275970Scy pfd.fd = up->fdt; 1978275970Scy rc = poll(&pfd, 1, 0); 1979275970Scy if (1 != rc || !(pfd.revents & POLLOUT)) 1980275970Scy return; 1981275970Scy } 1982275970Scy#elif defined(HAVE_SYS_SELECT_H) 1983275970Scy { 1984275970Scy struct timeval tout; 1985275970Scy fd_set wset; 1986275970Scy 1987275970Scy memset(&tout, 0, sizeof(tout)); 1988275970Scy FD_ZERO(&wset); 1989275970Scy FD_SET(up->fdt, &wset); 1990275970Scy rc = select(up->fdt+1, NULL, &wset, NULL, &tout); 1991275970Scy if (0 == rc || !(FD_ISSET(up->fdt, &wset))) 1992275970Scy return; 1993275970Scy } 1994275970Scy#else 1995275970Scy# error Blooper! That should have been found earlier! 1996275970Scy#endif 1997275970Scy 1998275970Scy /* next timeout is a full one... */ 1999275970Scy up->tickover = TICKOVER_LOW; 2000275970Scy 2001275970Scy /* check for socket error */ 2002275970Scy ec = 0; 2003275970Scy lc = sizeof(ec); 2004275970Scy rc = getsockopt(up->fdt, SOL_SOCKET, SO_ERROR, &ec, &lc); 2005275970Scy if (-1 == rc || 0 != ec) { 2006290000Sglebius const char *errtxt; 2007290000Sglebius if (0 == ec) 2008290000Sglebius ec = errno; 2009290000Sglebius errtxt = strerror(ec); 2010275970Scy if (syslogok(pp, up)) 2011275970Scy msyslog(LOG_ERR, 2012290000Sglebius "%s: async connect to GPSD failed," 2013290000Sglebius " fd=%d, ec=%d(%s)", 2014290000Sglebius up->logname, up->fdt, ec, errtxt); 2015290000Sglebius else 2016290000Sglebius DPRINTF(1, ("%s: async connect to GPSD failed," 2017290000Sglebius " fd=%d, ec=%d(%s)\n", 2018290000Sglebius up->logname, up->fdt, ec, errtxt)); 2019275970Scy goto no_socket; 2020290000Sglebius } else { 2021290000Sglebius DPRINTF(1, ("%s: async connect to GPSD succeeded, fd=%d\n", 2022290000Sglebius up->logname, up->fdt)); 2023290000Sglebius } 2024290000Sglebius 2025275970Scy /* swap socket FDs, and make sure the clock was added */ 2026275970Scy pp->io.fd = up->fdt; 2027275970Scy up->fdt = -1; 2028275970Scy if (0 == io_addclock(&pp->io)) { 2029275970Scy if (syslogok(pp, up)) 2030275970Scy msyslog(LOG_ERR, 2031275970Scy "%s: failed to register with I/O engine", 2032290000Sglebius up->logname); 2033275970Scy goto no_socket; 2034275970Scy } 2035275970Scy return; 2036290000Sglebius 2037275970Scy no_socket: 2038290000Sglebius if (-1 != up->fdt) { 2039290000Sglebius DPRINTF(1, ("%s: closing socket, fd=%d\n", 2040290000Sglebius up->logname, up->fdt)); 2041275970Scy close(up->fdt); 2042290000Sglebius } 2043275970Scy up->fdt = -1; 2044275970Scy up->tickover = up->tickpres; 2045275970Scy up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH); 2046275970Scy} 2047275970Scy 2048275970Scy/* ===================================================================== 2049275970Scy * helper stuff 2050275970Scy */ 2051275970Scy 2052290000Sglebius/* ------------------------------------------------------------------- 2053290000Sglebius * store a properly clamped precision value 2054275970Scy */ 2055290000Sglebiusstatic int16_t 2056290000Sglebiusclamped_precision( 2057290000Sglebius int rawprec) 2058275970Scy{ 2059290000Sglebius if (rawprec > 0) 2060290000Sglebius rawprec = 0; 2061290000Sglebius if (rawprec < -32) 2062290000Sglebius rawprec = -32; 2063290000Sglebius return (int16_t)rawprec; 2064275970Scy} 2065275970Scy 2066275970Scy/* ------------------------------------------------------------------- 2067290000Sglebius * Convert a GPSD timestamp (ISO8601 Format) to an l_fp 2068275970Scy */ 2069275970Scystatic BOOL 2070275970Scyconvert_ascii_time( 2071275970Scy l_fp * fp , 2072275970Scy const char * gps_time) 2073275970Scy{ 2074275970Scy char *ep; 2075275970Scy struct tm gd; 2076275970Scy struct timespec ts; 2077290000Sglebius uint32_t dw; 2078275970Scy 2079275970Scy /* Use 'strptime' to take the brunt of the work, then parse 2080275970Scy * the fractional part manually, starting with a digit weight of 2081275970Scy * 10^8 nanoseconds. 2082275970Scy */ 2083275970Scy ts.tv_nsec = 0; 2084275970Scy ep = strptime(gps_time, "%Y-%m-%dT%H:%M:%S", &gd); 2085290000Sglebius if (NULL == ep) 2086290000Sglebius return FALSE; /* could not parse the mandatory stuff! */ 2087275970Scy if (*ep == '.') { 2088290000Sglebius dw = 100000000u; 2089290000Sglebius while (isdigit(*(u_char*)++ep)) { 2090290000Sglebius ts.tv_nsec += (*(u_char*)ep - '0') * dw; 2091290000Sglebius dw /= 10u; 2092275970Scy } 2093275970Scy } 2094275970Scy if (ep[0] != 'Z' || ep[1] != '\0') 2095290000Sglebius return FALSE; /* trailing garbage */ 2096275970Scy 2097290000Sglebius /* Now convert the whole thing into a 'l_fp'. We do not use 2098290000Sglebius * 'mkgmtime()' since its not standard and going through the 2099290000Sglebius * calendar routines is not much effort, either. 2100290000Sglebius */ 2101275970Scy ts.tv_sec = (ntpcal_tm_to_rd(&gd) - DAY_NTP_STARTS) * SECSPERDAY 2102275970Scy + ntpcal_tm_to_daysec(&gd); 2103275970Scy *fp = tspec_intv_to_lfp(ts); 2104275970Scy 2105275970Scy return TRUE; 2106275970Scy} 2107275970Scy 2108275970Scy/* ------------------------------------------------------------------- 2109275970Scy * Save the last timecode string, making sure it's properly truncated 2110275970Scy * if necessary and NUL terminated in any case. 2111275970Scy */ 2112275970Scystatic void 2113275970Scysave_ltc( 2114275970Scy clockprocT * const pp, 2115275970Scy const char * const tc) 2116275970Scy{ 2117275970Scy size_t len; 2118275970Scy 2119275970Scy len = (tc) ? strlen(tc) : 0; 2120275970Scy if (len >= sizeof(pp->a_lastcode)) 2121275970Scy len = sizeof(pp->a_lastcode) - 1; 2122275970Scy pp->lencode = (u_short)len; 2123275970Scy memcpy(pp->a_lastcode, tc, len); 2124275970Scy pp->a_lastcode[len] = '\0'; 2125275970Scy} 2126275970Scy 2127290000Sglebius/* ------------------------------------------------------------------- 2128275970Scy * asprintf replacement... it's not available everywhere... 2129275970Scy */ 2130275970Scystatic int 2131275970Scymyasprintf( 2132275970Scy char ** spp, 2133275970Scy char const * fmt, 2134275970Scy ... ) 2135275970Scy{ 2136275970Scy size_t alen, plen; 2137275970Scy 2138275970Scy alen = 32; 2139275970Scy *spp = NULL; 2140275970Scy do { 2141275970Scy va_list va; 2142275970Scy 2143275970Scy alen += alen; 2144275970Scy free(*spp); 2145275970Scy *spp = (char*)malloc(alen); 2146275970Scy if (NULL == *spp) 2147275970Scy return -1; 2148275970Scy 2149275970Scy va_start(va, fmt); 2150275970Scy plen = (size_t)vsnprintf(*spp, alen, fmt, va); 2151275970Scy va_end(va); 2152275970Scy } while (plen >= alen); 2153275970Scy 2154275970Scy return (int)plen; 2155275970Scy} 2156275970Scy 2157290000Sglebius/* ------------------------------------------------------------------- 2158290000Sglebius * dump a raw data buffer 2159290000Sglebius */ 2160290000Sglebius 2161290000Sglebiusstatic char * 2162290000Sglebiusadd_string( 2163290000Sglebius char *dp, 2164290000Sglebius char *ep, 2165290000Sglebius const char *sp) 2166290000Sglebius{ 2167290000Sglebius while (dp != ep && *sp) 2168290000Sglebius *dp++ = *sp++; 2169290000Sglebius return dp; 2170290000Sglebius} 2171290000Sglebius 2172290000Sglebiusstatic void 2173290000Sglebiuslog_data( 2174290000Sglebius peerT *peer, 2175290000Sglebius const char *what, 2176290000Sglebius const char *buf , 2177290000Sglebius size_t len ) 2178290000Sglebius{ 2179290000Sglebius /* we're running single threaded with regards to the clocks. */ 2180290000Sglebius static char s_lbuf[2048]; 2181290000Sglebius 2182290000Sglebius clockprocT * const pp = peer->procptr; 2183290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 2184290000Sglebius 2185290000Sglebius if (debug > 1) { 2186290000Sglebius const char *sptr = buf; 2187290000Sglebius const char *stop = buf + len; 2188290000Sglebius char *dptr = s_lbuf; 2189290000Sglebius char *dtop = s_lbuf + sizeof(s_lbuf) - 1; /* for NUL */ 2190290000Sglebius 2191290000Sglebius while (sptr != stop && dptr != dtop) { 2192290000Sglebius u_char uch = (u_char)*sptr++; 2193290000Sglebius if (uch == '\\') { 2194290000Sglebius dptr = add_string(dptr, dtop, "\\\\"); 2195290000Sglebius } else if (isprint(uch)) { 2196290000Sglebius *dptr++ = (char)uch; 2197290000Sglebius } else { 2198290000Sglebius char fbuf[6]; 2199290000Sglebius snprintf(fbuf, sizeof(fbuf), "\\%03o", uch); 2200290000Sglebius dptr = add_string(dptr, dtop, fbuf); 2201290000Sglebius } 2202290000Sglebius } 2203290000Sglebius *dptr = '\0'; 2204290000Sglebius mprintf("%s[%s]: '%s'\n", up->logname, what, s_lbuf); 2205290000Sglebius } 2206290000Sglebius} 2207290000Sglebius 2208275970Scy#else 2209275970ScyNONEMPTY_TRANSLATION_UNIT 2210275970Scy#endif /* REFCLOCK && CLOCK_GPSDJSON */ 2211