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 380290000Sglebiusstatic const char * const s_req_version = 381290000Sglebius "?VERSION;\r\n"; 382290000Sglebius 383290000Sglebius/* We keep a static list of network addresses for 'localhost:gpsd' or a 384290000Sglebius * fallback alias of it, and we try to connect to them in round-robin 385290000Sglebius * fashion. The service lookup is done during the driver init 386290000Sglebius * function to minmise the impact of 'getaddrinfo()'. 387290000Sglebius * 388290000Sglebius * Alas, the init function is called even if there are no clocks 389290000Sglebius * configured for this driver. So it makes sense to defer the logging of 390290000Sglebius * any errors or other notifications until the first clock unit is 391290000Sglebius * started -- otherwise there might be syslog entries from a driver that 392290000Sglebius * is not used at all. 393275970Scy */ 394290000Sglebiusstatic addrinfoT *s_gpsd_addr; 395290000Sglebiusstatic gpsd_unitT *s_clock_units; 396275970Scy 397290000Sglebius/* list of service/socket names we want to resolve against */ 398290000Sglebiusstatic const char * const s_svctab[][2] = { 399290000Sglebius { "localhost", "gpsd" }, 400290000Sglebius { "localhost", "2947" }, 401290000Sglebius { "127.0.0.1", "2947" }, 402290000Sglebius { NULL, NULL } 403290000Sglebius}; 404290000Sglebius 405290000Sglebius/* list of address resolution errors and index of service entry that 406290000Sglebius * finally worked. 407290000Sglebius */ 408290000Sglebiusstatic int s_svcerr[sizeof(s_svctab)/sizeof(s_svctab[0])]; 409290000Sglebiusstatic int s_svcidx; 410290000Sglebius 411275970Scy/* ===================================================================== 412275970Scy * log throttling 413275970Scy */ 414275970Scystatic int/*BOOL*/ 415275970Scysyslogok( 416275970Scy clockprocT * const pp, 417275970Scy gpsd_unitT * const up) 418275970Scy{ 419275970Scy int res = (0 != (pp->sloppyclockflag & CLK_FLAG3)) 420275970Scy || (0 == up->logthrottle ) 421275970Scy || (LOGTHROTTLE == up->logthrottle ); 422275970Scy if (res) 423275970Scy up->logthrottle = LOGTHROTTLE; 424275970Scy return res; 425275970Scy} 426275970Scy 427275970Scy/* ===================================================================== 428275970Scy * the clock functions 429275970Scy */ 430275970Scy 431275970Scy/* --------------------------------------------------------------------- 432275970Scy * Init: This currently just gets the socket address for the GPS daemon 433275970Scy */ 434275970Scystatic void 435275970Scygpsd_init(void) 436275970Scy{ 437290000Sglebius addrinfoT hints; 438290000Sglebius int rc, idx; 439290000Sglebius 440290000Sglebius memset(s_svcerr, 0, sizeof(s_svcerr)); 441275970Scy memset(&hints, 0, sizeof(hints)); 442275970Scy hints.ai_family = AF_UNSPEC; 443275970Scy hints.ai_protocol = IPPROTO_TCP; 444275970Scy hints.ai_socktype = SOCK_STREAM; 445275970Scy 446290000Sglebius for (idx = 0; s_svctab[idx][0] && !s_gpsd_addr; idx++) { 447290000Sglebius rc = getaddrinfo(s_svctab[idx][0], s_svctab[idx][1], 448290000Sglebius &hints, &s_gpsd_addr); 449290000Sglebius s_svcerr[idx] = rc; 450290000Sglebius if (0 == rc) 451290000Sglebius break; 452275970Scy s_gpsd_addr = NULL; 453290000Sglebius } 454290000Sglebius s_svcidx = idx; 455275970Scy} 456275970Scy 457275970Scy/* --------------------------------------------------------------------- 458290000Sglebius * Init Check: flush pending log messages and check if we can proceed 459290000Sglebius */ 460290000Sglebiusstatic int/*BOOL*/ 461290000Sglebiusgpsd_init_check(void) 462290000Sglebius{ 463290000Sglebius int idx; 464290000Sglebius 465290000Sglebius /* Check if there is something to log */ 466290000Sglebius if (s_svcidx == 0) 467290000Sglebius return (s_gpsd_addr != NULL); 468290000Sglebius 469290000Sglebius /* spool out the resolver errors */ 470290000Sglebius for (idx = 0; idx < s_svcidx; ++idx) { 471290000Sglebius msyslog(LOG_WARNING, 472290000Sglebius "GPSD_JSON: failed to resolve '%s:%s', rc=%d (%s)", 473290000Sglebius s_svctab[idx][0], s_svctab[idx][1], 474290000Sglebius s_svcerr[idx], gai_strerror(s_svcerr[idx])); 475290000Sglebius } 476290000Sglebius 477290000Sglebius /* check if it was fatal, or if we can proceed */ 478290000Sglebius if (s_gpsd_addr == NULL) 479290000Sglebius msyslog(LOG_ERR, "%s", 480290000Sglebius "GPSD_JSON: failed to get socket address, giving up."); 481290000Sglebius else if (idx != 0) 482290000Sglebius msyslog(LOG_WARNING, 483290000Sglebius "GPSD_JSON: using '%s:%s' instead of '%s:%s'", 484290000Sglebius s_svctab[idx][0], s_svctab[idx][1], 485290000Sglebius s_svctab[0][0], s_svctab[0][1]); 486290000Sglebius 487290000Sglebius /* make sure this gets logged only once and tell if we can 488290000Sglebius * proceed or not 489290000Sglebius */ 490290000Sglebius s_svcidx = 0; 491290000Sglebius return (s_gpsd_addr != NULL); 492290000Sglebius} 493290000Sglebius 494290000Sglebius/* --------------------------------------------------------------------- 495275970Scy * Start: allocate a unit pointer and set up the runtime data 496275970Scy */ 497275970Scystatic int 498275970Scygpsd_start( 499275970Scy int unit, 500275970Scy peerT * peer) 501275970Scy{ 502290000Sglebius clockprocT * const pp = peer->procptr; 503290000Sglebius gpsd_unitT * up; 504290000Sglebius gpsd_unitT ** uscan = &s_clock_units; 505275970Scy 506275970Scy struct stat sb; 507275970Scy 508290000Sglebius /* check if we can proceed at all or if init failed */ 509290000Sglebius if ( ! gpsd_init_check()) 510290000Sglebius return FALSE; 511275970Scy 512290000Sglebius /* search for matching unit */ 513290000Sglebius while ((up = *uscan) != NULL && up->unit != (unit & 0x7F)) 514290000Sglebius uscan = &up->next_unit; 515290000Sglebius if (up == NULL) { 516290000Sglebius /* alloc unit, add to list and increment use count ASAP. */ 517290000Sglebius up = emalloc_zero(sizeof(*up)); 518290000Sglebius *uscan = up; 519290000Sglebius ++up->refcount; 520290000Sglebius 521290000Sglebius /* initialize the unit structure */ 522290000Sglebius up->logname = estrdup(refnumtoa(&peer->srcadr)); 523290000Sglebius up->unit = unit & 0x7F; 524290000Sglebius up->fdt = -1; 525290000Sglebius up->addr = s_gpsd_addr; 526290000Sglebius up->tickpres = TICKOVER_LOW; 527290000Sglebius 528290000Sglebius /* Create the device name and check for a Character 529290000Sglebius * Device. It's assumed that GPSD was started with the 530290000Sglebius * same link, so the names match. (If this is not 531290000Sglebius * practicable, we will have to read the symlink, if 532290000Sglebius * any, so we can get the true device file.) 533290000Sglebius */ 534290000Sglebius if (-1 == myasprintf(&up->device, "%s%u", 535290000Sglebius s_dev_stem, up->unit)) { 536290000Sglebius msyslog(LOG_ERR, "%s: clock device name too long", 537290000Sglebius up->logname); 538290000Sglebius goto dev_fail; 539290000Sglebius } 540290000Sglebius if (-1 == stat(up->device, &sb) || !S_ISCHR(sb.st_mode)) { 541290000Sglebius msyslog(LOG_ERR, "%s: '%s' is not a character device", 542290000Sglebius up->logname, up->device); 543290000Sglebius goto dev_fail; 544290000Sglebius } 545290000Sglebius } else { 546290000Sglebius /* All set up, just increment use count. */ 547290000Sglebius ++up->refcount; 548290000Sglebius } 549290000Sglebius 550275970Scy /* setup refclock processing */ 551275970Scy pp->unitptr = (caddr_t)up; 552290000Sglebius pp->io.fd = -1; 553275970Scy pp->io.clock_recv = gpsd_receive; 554275970Scy pp->io.srcclock = peer; 555275970Scy pp->io.datalen = 0; 556275970Scy pp->a_lastcode[0] = '\0'; 557275970Scy pp->lencode = 0; 558275970Scy pp->clockdesc = DESCRIPTION; 559275970Scy memcpy(&pp->refid, REFID, 4); 560275970Scy 561275970Scy /* Initialize miscellaneous variables */ 562290000Sglebius if (unit >= 128) 563290000Sglebius peer->precision = PPS_PRECISION; 564290000Sglebius else 565290000Sglebius peer->precision = PRECISION; 566275970Scy 567290000Sglebius /* If the daemon name lookup failed, just give up now. */ 568290000Sglebius if (NULL == up->addr) { 569290000Sglebius msyslog(LOG_ERR, "%s: no GPSD socket address, giving up", 570290000Sglebius up->logname); 571290000Sglebius goto dev_fail; 572275970Scy } 573290000Sglebius 574275970Scy LOGIF(CLOCKINFO, 575275970Scy (LOG_NOTICE, "%s: startup, device is '%s'", 576275970Scy refnumtoa(&peer->srcadr), up->device)); 577290000Sglebius up->mode = MODE_OP_MODE(peer->ttl); 578290000Sglebius if (up->mode > MODE_OP_MAXVAL) 579290000Sglebius up->mode = 0; 580290000Sglebius if (unit >= 128) 581290000Sglebius up->pps_peer = peer; 582290000Sglebius else 583290000Sglebius enter_opmode(peer, up->mode); 584275970Scy return TRUE; 585275970Scy 586275970Scydev_fail: 587275970Scy /* On failure, remove all UNIT ressources and declare defeat. */ 588275970Scy 589275970Scy INSIST (up); 590290000Sglebius if (!--up->refcount) { 591290000Sglebius *uscan = up->next_unit; 592290000Sglebius free(up->device); 593290000Sglebius free(up); 594290000Sglebius } 595275970Scy 596275970Scy pp->unitptr = (caddr_t)NULL; 597275970Scy return FALSE; 598275970Scy} 599275970Scy 600275970Scy/* ------------------------------------------------------------------ */ 601275970Scy 602275970Scystatic void 603275970Scygpsd_shutdown( 604275970Scy int unit, 605275970Scy peerT * peer) 606275970Scy{ 607275970Scy clockprocT * const pp = peer->procptr; 608275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 609290000Sglebius gpsd_unitT ** uscan = &s_clock_units; 610275970Scy 611275970Scy UNUSED_ARG(unit); 612275970Scy 613290000Sglebius /* The unit pointer might have been removed already. */ 614290000Sglebius if (up == NULL) 615290000Sglebius return; 616290000Sglebius 617290000Sglebius /* now check if we must close IO resources */ 618290000Sglebius if (peer != up->pps_peer) { 619290000Sglebius if (-1 != pp->io.fd) { 620290000Sglebius DPRINTF(1, ("%s: closing clock, fd=%d\n", 621290000Sglebius up->logname, pp->io.fd)); 622290000Sglebius io_closeclock(&pp->io); 623290000Sglebius pp->io.fd = -1; 624290000Sglebius } 625290000Sglebius if (up->fdt != -1) 626290000Sglebius close(up->fdt); 627275970Scy } 628290000Sglebius /* decrement use count and eventually remove this unit. */ 629290000Sglebius if (!--up->refcount) { 630290000Sglebius /* unlink this unit */ 631290000Sglebius while (*uscan != NULL) 632290000Sglebius if (*uscan == up) 633290000Sglebius *uscan = up->next_unit; 634290000Sglebius else 635290000Sglebius uscan = &(*uscan)->next_unit; 636290000Sglebius free(up->logname); 637290000Sglebius free(up->device); 638290000Sglebius free(up); 639290000Sglebius } 640275970Scy pp->unitptr = (caddr_t)NULL; 641275970Scy LOGIF(CLOCKINFO, 642275970Scy (LOG_NOTICE, "%s: shutdown", refnumtoa(&peer->srcadr))); 643275970Scy} 644275970Scy 645275970Scy/* ------------------------------------------------------------------ */ 646275970Scy 647275970Scystatic void 648275970Scygpsd_receive( 649275970Scy struct recvbuf * rbufp) 650275970Scy{ 651275970Scy /* declare & init control structure ptrs */ 652275970Scy peerT * const peer = rbufp->recv_peer; 653275970Scy clockprocT * const pp = peer->procptr; 654290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 655275970Scy 656275970Scy const char *psrc, *esrc; 657275970Scy char *pdst, *edst, ch; 658275970Scy 659290000Sglebius /* log the data stream, if this is enabled */ 660290000Sglebius log_data(peer, "recv", (const char*)rbufp->recv_buffer, 661290000Sglebius (size_t)rbufp->recv_length); 662290000Sglebius 663290000Sglebius 664275970Scy /* Since we're getting a raw stream data, we must assemble lines 665275970Scy * in our receive buffer. We can't use neither 'refclock_gtraw' 666275970Scy * not 'refclock_gtlin' here... We process chars until we reach 667275970Scy * an EoL (that is, line feed) but we truncate the message if it 668275970Scy * does not fit the buffer. GPSD might truncate messages, too, 669275970Scy * so dealing with truncated buffers is necessary anyway. 670275970Scy */ 671275970Scy psrc = (const char*)rbufp->recv_buffer; 672275970Scy esrc = psrc + rbufp->recv_length; 673275970Scy 674275970Scy pdst = up->buffer + up->buflen; 675275970Scy edst = pdst + sizeof(up->buffer) - 1; /* for trailing NUL */ 676275970Scy 677275970Scy while (psrc != esrc) { 678275970Scy ch = *psrc++; 679275970Scy if (ch == '\n') { 680275970Scy /* trim trailing whitespace & terminate buffer */ 681275970Scy while (pdst != up->buffer && pdst[-1] <= ' ') 682275970Scy --pdst; 683275970Scy *pdst = '\0'; 684275970Scy /* process data and reset buffer */ 685290000Sglebius up->buflen = pdst - up->buffer; 686275970Scy gpsd_parse(peer, &rbufp->recv_time); 687275970Scy pdst = up->buffer; 688275970Scy } else if (pdst != edst) { 689275970Scy /* add next char, ignoring leading whitespace */ 690275970Scy if (ch > ' ' || pdst != up->buffer) 691275970Scy *pdst++ = ch; 692275970Scy } 693275970Scy } 694275970Scy up->buflen = pdst - up->buffer; 695275970Scy up->tickover = TICKOVER_LOW; 696275970Scy} 697275970Scy 698275970Scy/* ------------------------------------------------------------------ */ 699275970Scy 700275970Scystatic void 701290000Sglebiuspoll_primary( 702290000Sglebius peerT * const peer , 703290000Sglebius clockprocT * const pp , 704290000Sglebius gpsd_unitT * const up ) 705275970Scy{ 706275970Scy if (pp->coderecv != pp->codeproc) { 707275970Scy /* all is well */ 708275970Scy pp->lastref = pp->lastrec; 709290000Sglebius refclock_report(peer, CEVNT_NOMINAL); 710275970Scy refclock_receive(peer); 711275970Scy } else { 712290000Sglebius /* Not working properly, admit to it. If we have no 713290000Sglebius * connection to GPSD, declare the clock as faulty. If 714290000Sglebius * there were bad replies, this is handled as the major 715290000Sglebius * cause, and everything else is just a timeout. 716290000Sglebius */ 717275970Scy peer->precision = PRECISION; 718290000Sglebius if (-1 == pp->io.fd) 719275970Scy refclock_report(peer, CEVNT_FAULT); 720290000Sglebius else if (0 != up->tc_breply) 721275970Scy refclock_report(peer, CEVNT_BADREPLY); 722290000Sglebius else 723275970Scy refclock_report(peer, CEVNT_TIMEOUT); 724275970Scy } 725275970Scy 726275970Scy if (pp->sloppyclockflag & CLK_FLAG4) 727290000Sglebius mprintf_clock_stats( 728290000Sglebius &peer->srcadr,"%u %u %u %u %u %u %u", 729290000Sglebius up->tc_recv, 730290000Sglebius up->tc_breply, up->tc_nosync, 731290000Sglebius up->tc_sti_recv, up->tc_sti_used, 732290000Sglebius up->tc_pps_recv, up->tc_pps_used); 733275970Scy 734275970Scy /* clear tallies for next round */ 735290000Sglebius up->tc_breply = 0; 736290000Sglebius up->tc_recv = 0; 737290000Sglebius up->tc_nosync = 0; 738290000Sglebius up->tc_sti_recv = 0; 739290000Sglebius up->tc_sti_used = 0; 740290000Sglebius up->tc_pps_recv = 0; 741290000Sglebius up->tc_pps_used = 0; 742275970Scy} 743275970Scy 744290000Sglebiusstatic void 745290000Sglebiuspoll_secondary( 746290000Sglebius peerT * const peer , 747290000Sglebius clockprocT * const pp , 748290000Sglebius gpsd_unitT * const up ) 749290000Sglebius{ 750290000Sglebius if (pp->coderecv != pp->codeproc) { 751290000Sglebius /* all is well */ 752290000Sglebius pp->lastref = pp->lastrec; 753290000Sglebius refclock_report(peer, CEVNT_NOMINAL); 754290000Sglebius refclock_receive(peer); 755290000Sglebius } else { 756290000Sglebius peer->precision = PPS_PRECISION; 757290000Sglebius peer->flags &= ~FLAG_PPS; 758290000Sglebius refclock_report(peer, CEVNT_TIMEOUT); 759290000Sglebius } 760290000Sglebius} 761290000Sglebius 762290000Sglebiusstatic void 763290000Sglebiusgpsd_poll( 764290000Sglebius int unit, 765290000Sglebius peerT * peer) 766290000Sglebius{ 767290000Sglebius clockprocT * const pp = peer->procptr; 768290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 769290000Sglebius 770290000Sglebius ++pp->polls; 771290000Sglebius if (peer == up->pps_peer) 772290000Sglebius poll_secondary(peer, pp, up); 773290000Sglebius else 774290000Sglebius poll_primary(peer, pp, up); 775290000Sglebius} 776290000Sglebius 777275970Scy/* ------------------------------------------------------------------ */ 778275970Scy 779275970Scystatic void 780275970Scygpsd_control( 781275970Scy int unit, 782275970Scy const struct refclockstat * in_st, 783275970Scy struct refclockstat * out_st, 784275970Scy peerT * peer ) 785275970Scy{ 786275970Scy clockprocT * const pp = peer->procptr; 787275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 788275970Scy 789290000Sglebius if (peer == up->pps_peer) { 790290000Sglebius DTOLFP(pp->fudgetime1, &up->pps_fudge2); 791290000Sglebius if ( ! (pp->sloppyclockflag & CLK_FLAG1)) 792290000Sglebius peer->flags &= ~FLAG_PPS; 793290000Sglebius } else { 794290000Sglebius /* save preprocessed fudge times */ 795290000Sglebius DTOLFP(pp->fudgetime1, &up->pps_fudge); 796290000Sglebius DTOLFP(pp->fudgetime2, &up->sti_fudge); 797275970Scy 798290000Sglebius if (MODE_OP_MODE(up->mode ^ peer->ttl)) { 799290000Sglebius leave_opmode(peer, up->mode); 800290000Sglebius up->mode = MODE_OP_MODE(peer->ttl); 801290000Sglebius enter_opmode(peer, up->mode); 802290000Sglebius } 803290000Sglebius } 804290000Sglebius } 805290000Sglebius 806275970Scy/* ------------------------------------------------------------------ */ 807275970Scy 808275970Scystatic void 809290000Sglebiustimer_primary( 810290000Sglebius peerT * const peer , 811290000Sglebius clockprocT * const pp , 812290000Sglebius gpsd_unitT * const up ) 813275970Scy{ 814290000Sglebius int rc; 815275970Scy 816275970Scy /* This is used for timeout handling. Nothing that needs 817275970Scy * sub-second precison happens here, so receive/connect/retry 818275970Scy * timeouts are simply handled by a count down, and then we 819275970Scy * decide what to do by the socket values. 820275970Scy * 821275970Scy * Note that the timer stays at zero here, unless some of the 822275970Scy * functions set it to another value. 823275970Scy */ 824275970Scy if (up->logthrottle) 825275970Scy --up->logthrottle; 826275970Scy if (up->tickover) 827275970Scy --up->tickover; 828275970Scy switch (up->tickover) { 829275970Scy case 4: 830290000Sglebius /* If we are connected to GPSD, try to get a live signal 831290000Sglebius * by querying the version. Otherwise just check the 832290000Sglebius * socket to become ready. 833275970Scy */ 834275970Scy if (-1 != pp->io.fd) { 835290000Sglebius size_t rlen = strlen(s_req_version); 836290000Sglebius DPRINTF(2, ("%s: timer livecheck: '%s'\n", 837290000Sglebius up->logname, s_req_version)); 838290000Sglebius log_data(peer, "send", s_req_version, rlen); 839290000Sglebius rc = write(pp->io.fd, s_req_version, rlen); 840290000Sglebius (void)rc; 841275970Scy } else if (-1 != up->fdt) { 842275970Scy gpsd_test_socket(peer); 843275970Scy } 844275970Scy break; 845275970Scy 846275970Scy case 0: 847275970Scy if (-1 != pp->io.fd) 848275970Scy gpsd_stop_socket(peer); 849275970Scy else if (-1 != up->fdt) 850275970Scy gpsd_test_socket(peer); 851275970Scy else if (NULL != s_gpsd_addr) 852275970Scy gpsd_init_socket(peer); 853275970Scy break; 854275970Scy 855275970Scy default: 856275970Scy if (-1 == pp->io.fd && -1 != up->fdt) 857275970Scy gpsd_test_socket(peer); 858275970Scy } 859290000Sglebius} 860275970Scy 861290000Sglebiusstatic void 862290000Sglebiustimer_secondary( 863290000Sglebius peerT * const peer , 864290000Sglebius clockprocT * const pp , 865290000Sglebius gpsd_unitT * const up ) 866290000Sglebius{ 867290000Sglebius /* Reduce the count by one. Flush sample buffer and clear PPS 868290000Sglebius * flag when this happens. 869290000Sglebius */ 870290000Sglebius up->ppscount2 = max(0, (up->ppscount2 - 1)); 871290000Sglebius if (0 == up->ppscount2) { 872290000Sglebius if (pp->coderecv != pp->codeproc) { 873290000Sglebius refclock_report(peer, CEVNT_TIMEOUT); 874290000Sglebius pp->coderecv = pp->codeproc; 875290000Sglebius } 876275970Scy peer->flags &= ~FLAG_PPS; 877290000Sglebius } 878275970Scy} 879275970Scy 880290000Sglebiusstatic void 881290000Sglebiusgpsd_timer( 882290000Sglebius int unit, 883290000Sglebius peerT * peer) 884290000Sglebius{ 885290000Sglebius clockprocT * const pp = peer->procptr; 886290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 887290000Sglebius 888290000Sglebius if (peer == up->pps_peer) 889290000Sglebius timer_secondary(peer, pp, up); 890290000Sglebius else 891290000Sglebius timer_primary(peer, pp, up); 892290000Sglebius} 893290000Sglebius 894275970Scy/* ===================================================================== 895290000Sglebius * handle opmode switches 896290000Sglebius */ 897290000Sglebius 898290000Sglebiusstatic void 899290000Sglebiusenter_opmode( 900290000Sglebius peerT *peer, 901290000Sglebius int mode) 902290000Sglebius{ 903290000Sglebius clockprocT * const pp = peer->procptr; 904290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 905290000Sglebius 906290000Sglebius DPRINTF(1, ("%s: enter operation mode %d\n", 907290000Sglebius up->logname, MODE_OP_MODE(mode))); 908290000Sglebius 909290000Sglebius if (MODE_OP_MODE(mode) == MODE_OP_AUTO) { 910290000Sglebius up->fl_rawsti = 0; 911290000Sglebius up->ppscount = PPS_MAXCOUNT / 2; 912290000Sglebius } 913290000Sglebius up->fl_pps = 0; 914290000Sglebius up->fl_sti = 0; 915290000Sglebius} 916290000Sglebius 917290000Sglebius/* ------------------------------------------------------------------ */ 918290000Sglebius 919290000Sglebiusstatic void 920290000Sglebiusleave_opmode( 921290000Sglebius peerT *peer, 922290000Sglebius int mode) 923290000Sglebius{ 924290000Sglebius clockprocT * const pp = peer->procptr; 925290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 926290000Sglebius 927290000Sglebius DPRINTF(1, ("%s: leaving operation mode %d\n", 928290000Sglebius up->logname, MODE_OP_MODE(mode))); 929290000Sglebius 930290000Sglebius if (MODE_OP_MODE(mode) == MODE_OP_AUTO) { 931290000Sglebius up->fl_rawsti = 0; 932290000Sglebius up->ppscount = 0; 933290000Sglebius } 934290000Sglebius up->fl_pps = 0; 935290000Sglebius up->fl_sti = 0; 936290000Sglebius} 937290000Sglebius 938290000Sglebius/* ===================================================================== 939290000Sglebius * operation mode specific evaluation 940290000Sglebius */ 941290000Sglebius 942290000Sglebiusstatic void 943290000Sglebiusadd_clock_sample( 944290000Sglebius peerT * const peer , 945290000Sglebius clockprocT * const pp , 946290000Sglebius l_fp stamp, 947290000Sglebius l_fp recvt) 948290000Sglebius{ 949290000Sglebius pp->lastref = stamp; 950290000Sglebius if (pp->coderecv == pp->codeproc) 951290000Sglebius refclock_report(peer, CEVNT_NOMINAL); 952310419Sdelphij refclock_process_offset(pp, stamp, recvt, pp->fudgetime1); 953290000Sglebius} 954290000Sglebius 955290000Sglebius/* ------------------------------------------------------------------ */ 956290000Sglebius 957290000Sglebiusstatic void 958290000Sglebiuseval_strict( 959290000Sglebius peerT * const peer , 960290000Sglebius clockprocT * const pp , 961290000Sglebius gpsd_unitT * const up ) 962290000Sglebius{ 963290000Sglebius if (up->fl_sti && up->fl_pps) { 964290000Sglebius /* use TPV reference time + PPS receive time */ 965290000Sglebius add_clock_sample(peer, pp, up->sti_stamp, up->pps_recvt); 966290000Sglebius peer->precision = up->pps_prec; 967290000Sglebius /* both packets consumed now... */ 968290000Sglebius up->fl_pps = 0; 969290000Sglebius up->fl_sti = 0; 970290000Sglebius ++up->tc_sti_used; 971290000Sglebius } 972290000Sglebius} 973290000Sglebius 974290000Sglebius/* ------------------------------------------------------------------ */ 975290000Sglebius/* PPS processing for the secondary channel. GPSD provides us with full 976290000Sglebius * timing information, so there's no danger of PLL-locking to the wrong 977290000Sglebius * second. The belts and suspenders needed for the raw ATOM clock are 978290000Sglebius * unnecessary here. 979290000Sglebius */ 980290000Sglebiusstatic void 981290000Sglebiuseval_pps_secondary( 982290000Sglebius peerT * const peer , 983290000Sglebius clockprocT * const pp , 984290000Sglebius gpsd_unitT * const up ) 985290000Sglebius{ 986290000Sglebius if (up->fl_pps2) { 987290000Sglebius /* feed data */ 988290000Sglebius add_clock_sample(peer, pp, up->pps_stamp2, up->pps_recvt2); 989290000Sglebius peer->precision = up->pps_prec; 990290000Sglebius /* PPS peer flag logic */ 991290000Sglebius up->ppscount2 = min(PPS2_MAXCOUNT, (up->ppscount2 + 2)); 992290000Sglebius if ((PPS2_MAXCOUNT == up->ppscount2) && 993290000Sglebius (pp->sloppyclockflag & CLK_FLAG1) ) 994290000Sglebius peer->flags |= FLAG_PPS; 995290000Sglebius /* mark time stamp as burned... */ 996290000Sglebius up->fl_pps2 = 0; 997290000Sglebius ++up->tc_pps_used; 998290000Sglebius } 999290000Sglebius} 1000290000Sglebius 1001290000Sglebius/* ------------------------------------------------------------------ */ 1002290000Sglebius 1003290000Sglebiusstatic void 1004290000Sglebiuseval_serial( 1005290000Sglebius peerT * const peer , 1006290000Sglebius clockprocT * const pp , 1007290000Sglebius gpsd_unitT * const up ) 1008290000Sglebius{ 1009290000Sglebius if (up->fl_sti) { 1010290000Sglebius add_clock_sample(peer, pp, up->sti_stamp, up->sti_recvt); 1011290000Sglebius peer->precision = up->sti_prec; 1012290000Sglebius /* mark time stamp as burned... */ 1013290000Sglebius up->fl_sti = 0; 1014290000Sglebius ++up->tc_sti_used; 1015290000Sglebius } 1016290000Sglebius} 1017290000Sglebius 1018290000Sglebius/* ------------------------------------------------------------------ */ 1019290000Sglebiusstatic void 1020290000Sglebiuseval_auto( 1021290000Sglebius peerT * const peer , 1022290000Sglebius clockprocT * const pp , 1023290000Sglebius gpsd_unitT * const up ) 1024290000Sglebius{ 1025290000Sglebius /* If there's no TPV available, stop working here... */ 1026290000Sglebius if (!up->fl_sti) 1027290000Sglebius return; 1028290000Sglebius 1029290000Sglebius /* check how to handle STI+PPS: Can PPS be used to augment STI 1030290000Sglebius * (or vice versae), do we drop the sample because there is a 1031290000Sglebius * temporary missing PPS signal, or do we feed on STI time 1032290000Sglebius * stamps alone? 1033290000Sglebius * 1034290000Sglebius * Do a counter/threshold dance to decide how to proceed. 1035290000Sglebius */ 1036290000Sglebius if (up->fl_pps) { 1037290000Sglebius up->ppscount = min(PPS_MAXCOUNT, 1038290000Sglebius (up->ppscount + PPS_INCCOUNT)); 1039290000Sglebius if ((PPS_MAXCOUNT == up->ppscount) && up->fl_rawsti) { 1040290000Sglebius up->fl_rawsti = 0; 1041290000Sglebius msyslog(LOG_INFO, 1042290000Sglebius "%s: expect valid PPS from now", 1043290000Sglebius up->logname); 1044290000Sglebius } 1045290000Sglebius } else { 1046290000Sglebius up->ppscount = max(0, (up->ppscount - PPS_DECCOUNT)); 1047290000Sglebius if ((0 == up->ppscount) && !up->fl_rawsti) { 1048290000Sglebius up->fl_rawsti = -1; 1049290000Sglebius msyslog(LOG_WARNING, 1050290000Sglebius "%s: use TPV alone from now", 1051290000Sglebius up->logname); 1052290000Sglebius } 1053290000Sglebius } 1054290000Sglebius 1055290000Sglebius /* now eventually feed the sample */ 1056290000Sglebius if (up->fl_rawsti) 1057290000Sglebius eval_serial(peer, pp, up); 1058290000Sglebius else 1059290000Sglebius eval_strict(peer, pp, up); 1060290000Sglebius} 1061290000Sglebius 1062290000Sglebius/* ===================================================================== 1063275970Scy * JSON parsing stuff 1064275970Scy */ 1065275970Scy 1066290000Sglebius/* ------------------------------------------------------------------ */ 1067290000Sglebius/* Parse a decimal integer with a possible sign. Works like 'strtoll()' 1068290000Sglebius * or 'strtol()', but with a fixed base of 10 and without eating away 1069290000Sglebius * leading whitespace. For the error codes, the handling of the end 1070290000Sglebius * pointer and the return values see 'strtol()'. 1071290000Sglebius */ 1072290000Sglebiusstatic json_int 1073290000Sglebiusstrtojint( 1074290000Sglebius const char *cp, char **ep) 1075290000Sglebius{ 1076290000Sglebius json_uint accu, limit_lo, limit_hi; 1077290000Sglebius int flags; /* bit 0: overflow; bit 1: sign */ 1078290000Sglebius const char * hold; 1079275970Scy 1080290000Sglebius /* pointer union to circumvent a tricky/sticky const issue */ 1081290000Sglebius union { const char * c; char * v; } vep; 1082275970Scy 1083290000Sglebius /* store initial value of 'cp' -- see 'strtol()' */ 1084290000Sglebius vep.c = cp; 1085275970Scy 1086290000Sglebius /* Eat away an optional sign and set the limits accordingly: The 1087290000Sglebius * high limit is the maximum absolute value that can be returned, 1088290000Sglebius * and the low limit is the biggest value that does not cause an 1089290000Sglebius * overflow when multiplied with 10. Avoid negation overflows. 1090290000Sglebius */ 1091290000Sglebius if (*cp == '-') { 1092290000Sglebius cp += 1; 1093290000Sglebius flags = 2; 1094290000Sglebius limit_hi = (json_uint)-(JSON_INT_MIN + 1) + 1; 1095290000Sglebius } else { 1096290000Sglebius cp += (*cp == '+'); 1097290000Sglebius flags = 0; 1098290000Sglebius limit_hi = (json_uint)JSON_INT_MAX; 1099290000Sglebius } 1100290000Sglebius limit_lo = limit_hi / 10; 1101275970Scy 1102290000Sglebius /* Now try to convert a sequence of digits. */ 1103290000Sglebius hold = cp; 1104290000Sglebius accu = 0; 1105290000Sglebius while (isdigit(*(const u_char*)cp)) { 1106290000Sglebius flags |= (accu > limit_lo); 1107290000Sglebius accu = accu * 10 + (*(const u_char*)cp++ - '0'); 1108290000Sglebius flags |= (accu > limit_hi); 1109290000Sglebius } 1110290000Sglebius /* Check for empty conversion (no digits seen). */ 1111290000Sglebius if (hold != cp) 1112290000Sglebius vep.c = cp; 1113290000Sglebius else 1114290000Sglebius errno = EINVAL; /* accu is still zero */ 1115290000Sglebius /* Check for range overflow */ 1116290000Sglebius if (flags & 1) { 1117290000Sglebius errno = ERANGE; 1118290000Sglebius accu = limit_hi; 1119290000Sglebius } 1120290000Sglebius /* If possible, store back the end-of-conversion pointer */ 1121290000Sglebius if (ep) 1122290000Sglebius *ep = vep.v; 1123290000Sglebius /* If negative, return the negated result if the accu is not 1124290000Sglebius * zero. Avoid negation overflows. 1125290000Sglebius */ 1126290000Sglebius if ((flags & 2) && accu) 1127290000Sglebius return -(json_int)(accu - 1) - 1; 1128290000Sglebius else 1129290000Sglebius return (json_int)accu; 1130290000Sglebius} 1131290000Sglebius 1132275970Scy/* ------------------------------------------------------------------ */ 1133275970Scy 1134275970Scystatic tok_ref 1135275970Scyjson_token_skip( 1136275970Scy const json_ctx * ctx, 1137275970Scy tok_ref tid) 1138275970Scy{ 1139294904Sdelphij if (tid >= 0 && (u_int)tid < ctx->ntok) { 1140290000Sglebius int len = ctx->tok[tid].size; 1141290000Sglebius /* For arrays and objects, the size is the number of 1142290000Sglebius * ITEMS in the compound. Thats the number of objects in 1143290000Sglebius * the array, and the number of key/value pairs for 1144290000Sglebius * objects. In theory, the key must be a string, and we 1145290000Sglebius * could simply skip one token before skipping the 1146290000Sglebius * value, which can be anything. We're a bit paranoid 1147290000Sglebius * and lazy at the same time: We simply double the 1148290000Sglebius * number of tokens to skip and fall through into the 1149290000Sglebius * array processing when encountering an object. 1150290000Sglebius */ 1151290000Sglebius switch (ctx->tok[tid].type) { 1152290000Sglebius case JSMN_OBJECT: 1153290000Sglebius len *= 2; 1154290000Sglebius /* FALLTHROUGH */ 1155290000Sglebius case JSMN_ARRAY: 1156290000Sglebius for (++tid; len; --len) 1157290000Sglebius tid = json_token_skip(ctx, tid); 1158275970Scy break; 1159290000Sglebius 1160290000Sglebius default: 1161290000Sglebius ++tid; 1162290000Sglebius break; 1163290000Sglebius } 1164294904Sdelphij /* The next condition should never be true, but paranoia 1165294904Sdelphij * prevails... 1166294904Sdelphij */ 1167294904Sdelphij if (tid < 0 || (u_int)tid > ctx->ntok) 1168290000Sglebius tid = ctx->ntok; 1169290000Sglebius } 1170275970Scy return tid; 1171275970Scy} 1172290000Sglebius 1173275970Scy/* ------------------------------------------------------------------ */ 1174275970Scy 1175275970Scystatic int 1176275970Scyjson_object_lookup( 1177290000Sglebius const json_ctx * ctx , 1178290000Sglebius tok_ref tid , 1179290000Sglebius const char * key , 1180290000Sglebius int what) 1181275970Scy{ 1182275970Scy int len; 1183275970Scy 1184290000Sglebius if (tid < 0 || tid >= ctx->ntok || 1185290000Sglebius ctx->tok[tid].type != JSMN_OBJECT) 1186275970Scy return INVALID_TOKEN; 1187290000Sglebius 1188290000Sglebius len = ctx->tok[tid].size; 1189290000Sglebius for (++tid; len && tid+1 < ctx->ntok; --len) { 1190290000Sglebius if (ctx->tok[tid].type != JSMN_STRING) { /* Blooper! */ 1191290000Sglebius tid = json_token_skip(ctx, tid); /* skip key */ 1192290000Sglebius tid = json_token_skip(ctx, tid); /* skip val */ 1193290000Sglebius } else if (strcmp(key, ctx->buf + ctx->tok[tid].start)) { 1194290000Sglebius tid = json_token_skip(ctx, tid+1); /* skip key+val */ 1195294904Sdelphij } else if (what < 0 || (u_int)what == ctx->tok[tid+1].type) { 1196275970Scy return tid + 1; 1197290000Sglebius } else { 1198290000Sglebius break; 1199290000Sglebius } 1200290000Sglebius /* if skipping ahead returned an error, bail out here. */ 1201290000Sglebius if (tid < 0) 1202290000Sglebius break; 1203275970Scy } 1204275970Scy return INVALID_TOKEN; 1205275970Scy} 1206275970Scy 1207275970Scy/* ------------------------------------------------------------------ */ 1208275970Scy 1209275970Scystatic const char* 1210290000Sglebiusjson_object_lookup_primitive( 1211290000Sglebius const json_ctx * ctx, 1212290000Sglebius tok_ref tid, 1213290000Sglebius const char * key) 1214290000Sglebius{ 1215290000Sglebius tid = json_object_lookup(ctx, tid, key, JSMN_PRIMITIVE); 1216290000Sglebius if (INVALID_TOKEN != tid) 1217290000Sglebius return ctx->buf + ctx->tok[tid].start; 1218290000Sglebius else 1219290000Sglebius return NULL; 1220290000Sglebius} 1221290000Sglebius/* ------------------------------------------------------------------ */ 1222290000Sglebius/* look up a boolean value. This essentially returns a tribool: 1223290000Sglebius * 0->false, 1->true, (-1)->error/undefined 1224290000Sglebius */ 1225290000Sglebiusstatic int 1226290000Sglebiusjson_object_lookup_bool( 1227290000Sglebius const json_ctx * ctx, 1228290000Sglebius tok_ref tid, 1229290000Sglebius const char * key) 1230290000Sglebius{ 1231290000Sglebius const char *cp; 1232290000Sglebius cp = json_object_lookup_primitive(ctx, tid, key); 1233290000Sglebius switch ( cp ? *cp : '\0') { 1234290000Sglebius case 't': return 1; 1235290000Sglebius case 'f': return 0; 1236290000Sglebius default : return -1; 1237290000Sglebius } 1238290000Sglebius} 1239290000Sglebius 1240290000Sglebius/* ------------------------------------------------------------------ */ 1241290000Sglebius 1242290000Sglebiusstatic const char* 1243275970Scyjson_object_lookup_string( 1244275970Scy const json_ctx * ctx, 1245275970Scy tok_ref tid, 1246275970Scy const char * key) 1247275970Scy{ 1248290000Sglebius tid = json_object_lookup(ctx, tid, key, JSMN_STRING); 1249290000Sglebius if (INVALID_TOKEN != tid) 1250290000Sglebius return ctx->buf + ctx->tok[tid].start; 1251275970Scy return NULL; 1252275970Scy} 1253275970Scy 1254275970Scystatic const char* 1255275970Scyjson_object_lookup_string_default( 1256275970Scy const json_ctx * ctx, 1257275970Scy tok_ref tid, 1258275970Scy const char * key, 1259275970Scy const char * def) 1260275970Scy{ 1261290000Sglebius tid = json_object_lookup(ctx, tid, key, JSMN_STRING); 1262290000Sglebius if (INVALID_TOKEN != tid) 1263290000Sglebius return ctx->buf + ctx->tok[tid].start; 1264290000Sglebius return def; 1265275970Scy} 1266275970Scy 1267275970Scy/* ------------------------------------------------------------------ */ 1268275970Scy 1269275970Scystatic json_int 1270275970Scyjson_object_lookup_int( 1271275970Scy const json_ctx * ctx, 1272275970Scy tok_ref tid, 1273275970Scy const char * key) 1274275970Scy{ 1275290000Sglebius json_int ret; 1276290000Sglebius const char * cp; 1277290000Sglebius char * ep; 1278275970Scy 1279290000Sglebius cp = json_object_lookup_primitive(ctx, tid, key); 1280290000Sglebius if (NULL != cp) { 1281290000Sglebius ret = strtojint(cp, &ep); 1282290000Sglebius if (cp != ep && '\0' == *ep) 1283290000Sglebius return ret; 1284290000Sglebius } else { 1285290000Sglebius errno = EINVAL; 1286290000Sglebius } 1287275970Scy return 0; 1288275970Scy} 1289275970Scy 1290275970Scystatic json_int 1291275970Scyjson_object_lookup_int_default( 1292275970Scy const json_ctx * ctx, 1293275970Scy tok_ref tid, 1294275970Scy const char * key, 1295275970Scy json_int def) 1296275970Scy{ 1297290000Sglebius json_int ret; 1298290000Sglebius const char * cp; 1299290000Sglebius char * ep; 1300290000Sglebius 1301290000Sglebius cp = json_object_lookup_primitive(ctx, tid, key); 1302290000Sglebius if (NULL != cp) { 1303290000Sglebius ret = strtojint(cp, &ep); 1304290000Sglebius if (cp != ep && '\0' == *ep) 1305290000Sglebius return ret; 1306290000Sglebius } 1307290000Sglebius return def; 1308275970Scy} 1309275970Scy 1310275970Scy/* ------------------------------------------------------------------ */ 1311290000Sglebius#if 0 /* currently unused */ 1312275970Scystatic double 1313275970Scyjson_object_lookup_float( 1314275970Scy const json_ctx * ctx, 1315275970Scy tok_ref tid, 1316275970Scy const char * key) 1317275970Scy{ 1318290000Sglebius double ret; 1319290000Sglebius const char * cp; 1320290000Sglebius char * ep; 1321275970Scy 1322290000Sglebius cp = json_object_lookup_primitive(ctx, tid, key); 1323290000Sglebius if (NULL != cp) { 1324290000Sglebius ret = strtod(cp, &ep); 1325290000Sglebius if (cp != ep && '\0' == *ep) 1326290000Sglebius return ret; 1327290000Sglebius } else { 1328290000Sglebius errno = EINVAL; 1329290000Sglebius } 1330275970Scy return 0.0; 1331275970Scy} 1332290000Sglebius#endif 1333275970Scy 1334275970Scystatic double 1335275970Scyjson_object_lookup_float_default( 1336275970Scy const json_ctx * ctx, 1337275970Scy tok_ref tid, 1338275970Scy const char * key, 1339275970Scy double def) 1340275970Scy{ 1341290000Sglebius double ret; 1342290000Sglebius const char * cp; 1343290000Sglebius char * ep; 1344290000Sglebius 1345290000Sglebius cp = json_object_lookup_primitive(ctx, tid, key); 1346290000Sglebius if (NULL != cp) { 1347290000Sglebius ret = strtod(cp, &ep); 1348290000Sglebius if (cp != ep && '\0' == *ep) 1349290000Sglebius return ret; 1350290000Sglebius } 1351290000Sglebius return def; 1352275970Scy} 1353275970Scy 1354275970Scy/* ------------------------------------------------------------------ */ 1355275970Scy 1356275970Scystatic BOOL 1357275970Scyjson_parse_record( 1358275970Scy json_ctx * ctx, 1359290000Sglebius char * buf, 1360290000Sglebius size_t len) 1361275970Scy{ 1362275970Scy jsmn_parser jsm; 1363275970Scy int idx, rc; 1364275970Scy 1365275970Scy jsmn_init(&jsm); 1366290000Sglebius rc = jsmn_parse(&jsm, buf, len, ctx->tok, JSMN_MAXTOK); 1367290000Sglebius if (rc <= 0) 1368290000Sglebius return FALSE; 1369275970Scy ctx->buf = buf; 1370290000Sglebius ctx->ntok = rc; 1371275970Scy 1372290000Sglebius if (JSMN_OBJECT != ctx->tok[0].type) 1373290000Sglebius return FALSE; /* not object!?! */ 1374290000Sglebius 1375275970Scy /* Make all tokens NUL terminated by overwriting the 1376290000Sglebius * terminator symbol. Makes string compares and number parsing a 1377290000Sglebius * lot easier! 1378275970Scy */ 1379290000Sglebius for (idx = 0; idx < ctx->ntok; ++idx) 1380275970Scy if (ctx->tok[idx].end > ctx->tok[idx].start) 1381275970Scy ctx->buf[ctx->tok[idx].end] = '\0'; 1382275970Scy return TRUE; 1383275970Scy} 1384275970Scy 1385275970Scy 1386275970Scy/* ===================================================================== 1387275970Scy * static local helpers 1388275970Scy */ 1389290000Sglebiusstatic BOOL 1390290000Sglebiusget_binary_time( 1391290000Sglebius l_fp * const dest , 1392290000Sglebius json_ctx * const jctx , 1393290000Sglebius const char * const time_name, 1394290000Sglebius const char * const frac_name, 1395290000Sglebius long fscale ) 1396290000Sglebius{ 1397290000Sglebius BOOL retv = FALSE; 1398290000Sglebius struct timespec ts; 1399275970Scy 1400290000Sglebius errno = 0; 1401290000Sglebius ts.tv_sec = (time_t)json_object_lookup_int(jctx, 0, time_name); 1402290000Sglebius ts.tv_nsec = (long )json_object_lookup_int(jctx, 0, frac_name); 1403290000Sglebius if (0 == errno) { 1404290000Sglebius ts.tv_nsec *= fscale; 1405290000Sglebius *dest = tspec_stamp_to_lfp(ts); 1406290000Sglebius retv = TRUE; 1407290000Sglebius } 1408290000Sglebius return retv; 1409290000Sglebius} 1410290000Sglebius 1411275970Scy/* ------------------------------------------------------------------ */ 1412275970Scy/* Process a WATCH record 1413275970Scy * 1414275970Scy * Currently this is only used to recognise that the device is present 1415275970Scy * and that we're listed subscribers. 1416275970Scy */ 1417275970Scystatic void 1418275970Scyprocess_watch( 1419275970Scy peerT * const peer , 1420275970Scy json_ctx * const jctx , 1421275970Scy const l_fp * const rtime) 1422275970Scy{ 1423275970Scy clockprocT * const pp = peer->procptr; 1424275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1425275970Scy 1426290000Sglebius const char * path; 1427290000Sglebius 1428290000Sglebius path = json_object_lookup_string(jctx, 0, "device"); 1429290000Sglebius if (NULL == path || strcmp(path, up->device)) 1430290000Sglebius return; 1431290000Sglebius 1432290000Sglebius if (json_object_lookup_bool(jctx, 0, "enable") > 0 && 1433290000Sglebius json_object_lookup_bool(jctx, 0, "json" ) > 0 ) 1434290000Sglebius up->fl_watch = -1; 1435290000Sglebius else 1436290000Sglebius up->fl_watch = 0; 1437290000Sglebius DPRINTF(2, ("%s: process_watch, enabled=%d\n", 1438290000Sglebius up->logname, (up->fl_watch & 1))); 1439275970Scy} 1440275970Scy 1441275970Scy/* ------------------------------------------------------------------ */ 1442275970Scy 1443275970Scystatic void 1444275970Scyprocess_version( 1445275970Scy peerT * const peer , 1446275970Scy json_ctx * const jctx , 1447275970Scy const l_fp * const rtime) 1448275970Scy{ 1449275970Scy clockprocT * const pp = peer->procptr; 1450275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1451275970Scy 1452275970Scy int len; 1453275970Scy char * buf; 1454275970Scy const char *revision; 1455275970Scy const char *release; 1456290000Sglebius uint16_t pvhi, pvlo; 1457275970Scy 1458275970Scy /* get protocol version number */ 1459275970Scy revision = json_object_lookup_string_default( 1460290000Sglebius jctx, 0, "rev", "(unknown)"); 1461275970Scy release = json_object_lookup_string_default( 1462290000Sglebius jctx, 0, "release", "(unknown)"); 1463275970Scy errno = 0; 1464290000Sglebius pvhi = (uint16_t)json_object_lookup_int(jctx, 0, "proto_major"); 1465290000Sglebius pvlo = (uint16_t)json_object_lookup_int(jctx, 0, "proto_minor"); 1466290000Sglebius 1467275970Scy if (0 == errno) { 1468290000Sglebius if ( ! up->fl_vers) 1469290000Sglebius msyslog(LOG_INFO, 1470290000Sglebius "%s: GPSD revision=%s release=%s protocol=%u.%u", 1471290000Sglebius up->logname, revision, release, 1472290000Sglebius pvhi, pvlo); 1473290000Sglebius up->proto_version = PROTO_VERSION(pvhi, pvlo); 1474275970Scy up->fl_vers = -1; 1475290000Sglebius } else { 1476275970Scy if (syslogok(pp, up)) 1477275970Scy msyslog(LOG_INFO, 1478290000Sglebius "%s: could not evaluate version data", 1479290000Sglebius up->logname); 1480290000Sglebius return; 1481275970Scy } 1482290000Sglebius /* With the 3.9 GPSD protocol, '*_musec' vanished from the PPS 1483290000Sglebius * record and was replace by '*_nsec'. 1484290000Sglebius */ 1485290000Sglebius up->pf_nsec = -(up->proto_version >= PROTO_VERSION(3,9)); 1486275970Scy 1487290000Sglebius /* With the 3.10 protocol we can get TOFF records for better 1488290000Sglebius * timing information. 1489275970Scy */ 1490290000Sglebius up->pf_toff = -(up->proto_version >= PROTO_VERSION(3,10)); 1491275970Scy 1492290000Sglebius /* request watch for our GPS device if not yet watched. 1493290000Sglebius * 1494290000Sglebius * The version string is also sent as a life signal, if we have 1495290000Sglebius * seen useable data. So if we're already watching the device, 1496290000Sglebius * skip the request. 1497290000Sglebius * 1498275970Scy * Reuse the input buffer, which is no longer needed in the 1499275970Scy * current cycle. Also assume that we can write the watch 1500275970Scy * request in one sweep into the socket; since we do not do 1501275970Scy * output otherwise, this should always work. (Unless the 1502275970Scy * TCP/IP window size gets lower than the length of the 1503275970Scy * request. We handle that when it happens.) 1504275970Scy */ 1505290000Sglebius if (up->fl_watch) 1506290000Sglebius return; 1507290000Sglebius 1508294904Sdelphij /* The logon string is actually the ?WATCH command of GPSD, 1509294904Sdelphij * using JSON data and selecting the GPS device name we created 1510294904Sdelphij * from our unit number. We have an old a newer version that 1511294904Sdelphij * request PPS (and TOFF) transmission. 1512294904Sdelphij */ 1513275970Scy snprintf(up->buffer, sizeof(up->buffer), 1514294904Sdelphij "?WATCH={\"device\":\"%s\",\"enable\":true,\"json\":true%s};\r\n", 1515294904Sdelphij up->device, (up->pf_toff ? ",\"pps\":true" : "")); 1516275970Scy buf = up->buffer; 1517275970Scy len = strlen(buf); 1518290000Sglebius log_data(peer, "send", buf, len); 1519290000Sglebius if (len != write(pp->io.fd, buf, len) && (syslogok(pp, up))) { 1520290000Sglebius /* Note: if the server fails to read our request, the 1521275970Scy * resulting data timeout will take care of the 1522275970Scy * connection! 1523275970Scy */ 1524290000Sglebius msyslog(LOG_ERR, "%s: failed to write watch request (%m)", 1525290000Sglebius up->logname); 1526275970Scy } 1527275970Scy} 1528275970Scy 1529275970Scy/* ------------------------------------------------------------------ */ 1530275970Scy 1531275970Scystatic void 1532275970Scyprocess_tpv( 1533275970Scy peerT * const peer , 1534275970Scy json_ctx * const jctx , 1535275970Scy const l_fp * const rtime) 1536275970Scy{ 1537275970Scy clockprocT * const pp = peer->procptr; 1538275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1539275970Scy 1540275970Scy const char * gps_time; 1541275970Scy int gps_mode; 1542290000Sglebius double ept; 1543280849Scy int xlog2; 1544275970Scy 1545275970Scy gps_mode = (int)json_object_lookup_int_default( 1546275970Scy jctx, 0, "mode", 0); 1547275970Scy 1548290000Sglebius gps_time = json_object_lookup_string( 1549290000Sglebius jctx, 0, "time"); 1550275970Scy 1551290000Sglebius /* accept time stamps only in 2d or 3d fix */ 1552290000Sglebius if (gps_mode < 2 || NULL == gps_time) { 1553275970Scy /* receiver has no fix; tell about and avoid stale data */ 1554290000Sglebius if ( ! up->pf_toff) 1555290000Sglebius ++up->tc_sti_recv; 1556290000Sglebius ++up->tc_nosync; 1557290000Sglebius up->fl_sti = 0; 1558290000Sglebius up->fl_pps = 0; 1559290000Sglebius up->fl_nosync = -1; 1560275970Scy return; 1561275970Scy } 1562290000Sglebius up->fl_nosync = 0; 1563275970Scy 1564290000Sglebius /* convert clock and set resulting ref time, but only if the 1565290000Sglebius * TOFF sentence is *not* available 1566290000Sglebius */ 1567290000Sglebius if ( ! up->pf_toff) { 1568290000Sglebius ++up->tc_sti_recv; 1569290000Sglebius /* save last time code to clock data */ 1570290000Sglebius save_ltc(pp, gps_time); 1571290000Sglebius /* now parse the time string */ 1572290000Sglebius if (convert_ascii_time(&up->sti_stamp, gps_time)) { 1573290000Sglebius DPRINTF(2, ("%s: process_tpv, stamp='%s'," 1574290000Sglebius " recvt='%s' mode=%u\n", 1575290000Sglebius up->logname, 1576290000Sglebius gmprettydate(&up->sti_stamp), 1577290000Sglebius gmprettydate(&up->sti_recvt), 1578290000Sglebius gps_mode)); 1579275970Scy 1580290000Sglebius /* have to use local receive time as substitute 1581290000Sglebius * for the real receive time: TPV does not tell 1582290000Sglebius * us. 1583290000Sglebius */ 1584290000Sglebius up->sti_local = *rtime; 1585290000Sglebius up->sti_recvt = *rtime; 1586290000Sglebius L_SUB(&up->sti_recvt, &up->sti_fudge); 1587290000Sglebius up->fl_sti = -1; 1588290000Sglebius } else { 1589290000Sglebius ++up->tc_breply; 1590290000Sglebius up->fl_sti = 0; 1591290000Sglebius } 1592275970Scy } 1593290000Sglebius 1594275970Scy /* Set the precision from the GPSD data 1595290000Sglebius * Use the ETP field for an estimation of the precision of the 1596290000Sglebius * serial data. If ETP is not available, use the default serial 1597290000Sglebius * data presion instead. (Note: The PPS branch has a different 1598290000Sglebius * precision estimation, since it gets the proper value directly 1599290000Sglebius * from GPSD!) 1600275970Scy */ 1601290000Sglebius ept = json_object_lookup_float_default(jctx, 0, "ept", 2.0e-3); 1602290000Sglebius ept = frexp(fabs(ept)*0.70710678, &xlog2); /* ~ sqrt(0.5) */ 1603290000Sglebius if (ept < 0.25) 1604290000Sglebius xlog2 = INT_MIN; 1605290000Sglebius if (ept > 2.0) 1606290000Sglebius xlog2 = INT_MAX; 1607290000Sglebius up->sti_prec = clamped_precision(xlog2); 1608275970Scy} 1609275970Scy 1610275970Scy/* ------------------------------------------------------------------ */ 1611275970Scy 1612275970Scystatic void 1613275970Scyprocess_pps( 1614275970Scy peerT * const peer , 1615275970Scy json_ctx * const jctx , 1616275970Scy const l_fp * const rtime) 1617275970Scy{ 1618275970Scy clockprocT * const pp = peer->procptr; 1619275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1620275970Scy 1621290000Sglebius int xlog2; 1622275970Scy 1623290000Sglebius ++up->tc_pps_recv; 1624275970Scy 1625290000Sglebius /* Bail out if there's indication that time sync is bad or 1626290000Sglebius * if we're explicitely requested to ignore PPS data. 1627290000Sglebius */ 1628290000Sglebius if (up->fl_nosync) 1629290000Sglebius return; 1630290000Sglebius 1631275970Scy up->pps_local = *rtime; 1632290000Sglebius /* Now grab the time values. 'clock_*' is the event time of the 1633290000Sglebius * pulse measured on the local system clock; 'real_*' is the GPS 1634290000Sglebius * reference time GPSD associated with the pulse. 1635290000Sglebius */ 1636290000Sglebius if (up->pf_nsec) { 1637290000Sglebius if ( ! get_binary_time(&up->pps_recvt2, jctx, 1638290000Sglebius "clock_sec", "clock_nsec", 1)) 1639290000Sglebius goto fail; 1640290000Sglebius if ( ! get_binary_time(&up->pps_stamp2, jctx, 1641290000Sglebius "real_sec", "real_nsec", 1)) 1642290000Sglebius goto fail; 1643290000Sglebius } else { 1644290000Sglebius if ( ! get_binary_time(&up->pps_recvt2, jctx, 1645290000Sglebius "clock_sec", "clock_musec", 1000)) 1646290000Sglebius goto fail; 1647290000Sglebius if ( ! get_binary_time(&up->pps_stamp2, jctx, 1648290000Sglebius "real_sec", "real_musec", 1000)) 1649290000Sglebius goto fail; 1650290000Sglebius } 1651275970Scy 1652290000Sglebius /* Try to read the precision field from the PPS record. If it's 1653290000Sglebius * not there, take the precision from the serial data. 1654290000Sglebius */ 1655290000Sglebius xlog2 = json_object_lookup_int_default( 1656290000Sglebius jctx, 0, "precision", up->sti_prec); 1657290000Sglebius up->pps_prec = clamped_precision(xlog2); 1658290000Sglebius 1659290000Sglebius /* Get fudged receive times for primary & secondary unit */ 1660290000Sglebius up->pps_recvt = up->pps_recvt2; 1661290000Sglebius L_SUB(&up->pps_recvt , &up->pps_fudge ); 1662290000Sglebius L_SUB(&up->pps_recvt2, &up->pps_fudge2); 1663290000Sglebius pp->lastrec = up->pps_recvt; 1664290000Sglebius 1665290000Sglebius /* Map to nearest full second as reference time stamp for the 1666290000Sglebius * primary channel. Sanity checks are done in evaluation step. 1667290000Sglebius */ 1668275970Scy up->pps_stamp = up->pps_recvt; 1669275970Scy L_ADDUF(&up->pps_stamp, 0x80000000u); 1670275970Scy up->pps_stamp.l_uf = 0; 1671275970Scy 1672290000Sglebius if (NULL != up->pps_peer) 1673290000Sglebius save_ltc(up->pps_peer->procptr, 1674290000Sglebius gmprettydate(&up->pps_stamp2)); 1675290000Sglebius DPRINTF(2, ("%s: PPS record processed," 1676290000Sglebius " stamp='%s', recvt='%s'\n", 1677290000Sglebius up->logname, 1678290000Sglebius gmprettydate(&up->pps_stamp2), 1679290000Sglebius gmprettydate(&up->pps_recvt2))); 1680275970Scy 1681290000Sglebius up->fl_pps = (0 != (pp->sloppyclockflag & CLK_FLAG2)) - 1; 1682290000Sglebius up->fl_pps2 = -1; 1683275970Scy return; 1684275970Scy 1685275970Scy fail: 1686290000Sglebius DPRINTF(1, ("%s: PPS record processing FAILED\n", 1687290000Sglebius up->logname)); 1688290000Sglebius ++up->tc_breply; 1689275970Scy} 1690275970Scy 1691275970Scy/* ------------------------------------------------------------------ */ 1692275970Scy 1693275970Scystatic void 1694290000Sglebiusprocess_toff( 1695290000Sglebius peerT * const peer , 1696290000Sglebius json_ctx * const jctx , 1697290000Sglebius const l_fp * const rtime) 1698290000Sglebius{ 1699290000Sglebius clockprocT * const pp = peer->procptr; 1700290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1701290000Sglebius 1702290000Sglebius ++up->tc_sti_recv; 1703290000Sglebius 1704290000Sglebius /* remember this! */ 1705290000Sglebius up->pf_toff = -1; 1706290000Sglebius 1707290000Sglebius /* bail out if there's indication that time sync is bad */ 1708290000Sglebius if (up->fl_nosync) 1709290000Sglebius return; 1710290000Sglebius 1711290000Sglebius if ( ! get_binary_time(&up->sti_recvt, jctx, 1712290000Sglebius "clock_sec", "clock_nsec", 1)) 1713290000Sglebius goto fail; 1714290000Sglebius if ( ! get_binary_time(&up->sti_stamp, jctx, 1715290000Sglebius "real_sec", "real_nsec", 1)) 1716290000Sglebius goto fail; 1717290000Sglebius L_SUB(&up->sti_recvt, &up->sti_fudge); 1718290000Sglebius up->sti_local = *rtime; 1719290000Sglebius up->fl_sti = -1; 1720290000Sglebius 1721290000Sglebius save_ltc(pp, gmprettydate(&up->sti_stamp)); 1722290000Sglebius DPRINTF(2, ("%s: TOFF record processed," 1723290000Sglebius " stamp='%s', recvt='%s'\n", 1724290000Sglebius up->logname, 1725290000Sglebius gmprettydate(&up->sti_stamp), 1726290000Sglebius gmprettydate(&up->sti_recvt))); 1727290000Sglebius return; 1728290000Sglebius 1729290000Sglebius fail: 1730290000Sglebius DPRINTF(1, ("%s: TOFF record processing FAILED\n", 1731290000Sglebius up->logname)); 1732290000Sglebius ++up->tc_breply; 1733290000Sglebius} 1734290000Sglebius 1735290000Sglebius/* ------------------------------------------------------------------ */ 1736290000Sglebius 1737290000Sglebiusstatic void 1738275970Scygpsd_parse( 1739275970Scy peerT * const peer , 1740275970Scy const l_fp * const rtime) 1741275970Scy{ 1742275970Scy clockprocT * const pp = peer->procptr; 1743275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1744275970Scy 1745275970Scy const char * clsid; 1746275970Scy 1747290000Sglebius DPRINTF(2, ("%s: gpsd_parse: time %s '%.*s'\n", 1748290000Sglebius up->logname, ulfptoa(rtime, 6), 1749290000Sglebius up->buflen, up->buffer)); 1750275970Scy 1751290000Sglebius /* See if we can grab anything potentially useful. JSMN does not 1752290000Sglebius * need a trailing NUL, but it needs the number of bytes to 1753290000Sglebius * process. */ 1754290000Sglebius if (!json_parse_record(&up->json_parse, up->buffer, up->buflen)) { 1755290000Sglebius ++up->tc_breply; 1756275970Scy return; 1757290000Sglebius } 1758290000Sglebius 1759275970Scy /* Now dispatch over the objects we know */ 1760290000Sglebius clsid = json_object_lookup_string(&up->json_parse, 0, "class"); 1761290000Sglebius if (NULL == clsid) { 1762290000Sglebius ++up->tc_breply; 1763290000Sglebius return; 1764290000Sglebius } 1765275970Scy 1766290000Sglebius if (!strcmp("TPV", clsid)) 1767290000Sglebius process_tpv(peer, &up->json_parse, rtime); 1768275970Scy else if (!strcmp("PPS", clsid)) 1769290000Sglebius process_pps(peer, &up->json_parse, rtime); 1770290000Sglebius else if (!strcmp("TOFF", clsid)) 1771290000Sglebius process_toff(peer, &up->json_parse, rtime); 1772290000Sglebius else if (!strcmp("VERSION", clsid)) 1773290000Sglebius process_version(peer, &up->json_parse, rtime); 1774275970Scy else if (!strcmp("WATCH", clsid)) 1775290000Sglebius process_watch(peer, &up->json_parse, rtime); 1776275970Scy else 1777275970Scy return; /* nothing we know about... */ 1778290000Sglebius ++up->tc_recv; 1779275970Scy 1780290000Sglebius /* if possible, feed the PPS side channel */ 1781290000Sglebius if (up->pps_peer) 1782290000Sglebius eval_pps_secondary( 1783290000Sglebius up->pps_peer, up->pps_peer->procptr, up); 1784275970Scy 1785290000Sglebius /* check PPS vs. STI receive times: 1786290000Sglebius * If STI is before PPS, then clearly the STI is too old. If PPS 1787290000Sglebius * is before STI by more than one second, then PPS is too old. 1788290000Sglebius * Weed out stale time stamps & flags. 1789290000Sglebius */ 1790290000Sglebius if (up->fl_pps && up->fl_sti) { 1791290000Sglebius l_fp diff; 1792290000Sglebius diff = up->sti_local; 1793290000Sglebius L_SUB(&diff, &up->pps_local); 1794290000Sglebius if (diff.l_i > 0) 1795290000Sglebius up->fl_pps = 0; /* pps too old */ 1796290000Sglebius else if (diff.l_i < 0) 1797290000Sglebius up->fl_sti = 0; /* serial data too old */ 1798275970Scy } 1799290000Sglebius 1800290000Sglebius /* dispatch to the mode-dependent processing functions */ 1801290000Sglebius switch (up->mode) { 1802290000Sglebius default: 1803290000Sglebius case MODE_OP_STI: 1804290000Sglebius eval_serial(peer, pp, up); 1805290000Sglebius break; 1806290000Sglebius 1807290000Sglebius case MODE_OP_STRICT: 1808290000Sglebius eval_strict(peer, pp, up); 1809290000Sglebius break; 1810290000Sglebius 1811290000Sglebius case MODE_OP_AUTO: 1812290000Sglebius eval_auto(peer, pp, up); 1813290000Sglebius break; 1814290000Sglebius } 1815275970Scy} 1816275970Scy 1817275970Scy/* ------------------------------------------------------------------ */ 1818275970Scy 1819275970Scystatic void 1820275970Scygpsd_stop_socket( 1821275970Scy peerT * const peer) 1822275970Scy{ 1823275970Scy clockprocT * const pp = peer->procptr; 1824275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1825275970Scy 1826290000Sglebius if (-1 != pp->io.fd) { 1827290000Sglebius if (syslogok(pp, up)) 1828290000Sglebius msyslog(LOG_INFO, 1829290000Sglebius "%s: closing socket to GPSD, fd=%d", 1830290000Sglebius up->logname, pp->io.fd); 1831290000Sglebius else 1832290000Sglebius DPRINTF(1, ("%s: closing socket to GPSD, fd=%d\n", 1833290000Sglebius up->logname, pp->io.fd)); 1834275970Scy io_closeclock(&pp->io); 1835290000Sglebius pp->io.fd = -1; 1836290000Sglebius } 1837275970Scy up->tickover = up->tickpres; 1838275970Scy up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH); 1839275970Scy up->fl_vers = 0; 1840290000Sglebius up->fl_sti = 0; 1841275970Scy up->fl_pps = 0; 1842275970Scy up->fl_watch = 0; 1843275970Scy} 1844275970Scy 1845275970Scy/* ------------------------------------------------------------------ */ 1846275970Scy 1847275970Scystatic void 1848275970Scygpsd_init_socket( 1849275970Scy peerT * const peer) 1850275970Scy{ 1851275970Scy clockprocT * const pp = peer->procptr; 1852275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1853275970Scy addrinfoT * ai; 1854275970Scy int rc; 1855275970Scy int ov; 1856275970Scy 1857275970Scy /* draw next address to try */ 1858275970Scy if (NULL == up->addr) 1859275970Scy up->addr = s_gpsd_addr; 1860275970Scy ai = up->addr; 1861275970Scy up->addr = ai->ai_next; 1862275970Scy 1863275970Scy /* try to create a matching socket */ 1864275970Scy up->fdt = socket( 1865275970Scy ai->ai_family, ai->ai_socktype, ai->ai_protocol); 1866275970Scy if (-1 == up->fdt) { 1867275970Scy if (syslogok(pp, up)) 1868275970Scy msyslog(LOG_ERR, 1869275970Scy "%s: cannot create GPSD socket: %m", 1870290000Sglebius up->logname); 1871275970Scy goto no_socket; 1872275970Scy } 1873290000Sglebius 1874290000Sglebius /* Make sure the socket is non-blocking. Connect/reconnect and 1875290000Sglebius * IO happen in an event-driven environment, and synchronous 1876290000Sglebius * operations wreak havoc on that. 1877290000Sglebius */ 1878275970Scy rc = fcntl(up->fdt, F_SETFL, O_NONBLOCK, 1); 1879275970Scy if (-1 == rc) { 1880275970Scy if (syslogok(pp, up)) 1881275970Scy msyslog(LOG_ERR, 1882275970Scy "%s: cannot set GPSD socket to non-blocking: %m", 1883290000Sglebius up->logname); 1884275970Scy goto no_socket; 1885275970Scy } 1886290000Sglebius /* Disable nagling. The way both GPSD and NTPD handle the 1887290000Sglebius * protocol makes it record-oriented, and in most cases 1888290000Sglebius * complete records (JSON serialised objects) will be sent in 1889290000Sglebius * one sweep. Nagling gives not much advantage but adds another 1890290000Sglebius * delay, which can worsen the situation for some packets. 1891290000Sglebius */ 1892275970Scy ov = 1; 1893275970Scy rc = setsockopt(up->fdt, IPPROTO_TCP, TCP_NODELAY, 1894275970Scy (char*)&ov, sizeof(ov)); 1895275970Scy if (-1 == rc) { 1896275970Scy if (syslogok(pp, up)) 1897275970Scy msyslog(LOG_INFO, 1898275970Scy "%s: cannot disable TCP nagle: %m", 1899290000Sglebius up->logname); 1900275970Scy } 1901275970Scy 1902290000Sglebius /* Start a non-blocking connect. There might be a synchronous 1903290000Sglebius * connection result we have to handle. 1904290000Sglebius */ 1905275970Scy rc = connect(up->fdt, ai->ai_addr, ai->ai_addrlen); 1906290000Sglebius if (-1 == rc) { 1907290000Sglebius if (errno == EINPROGRESS) { 1908290000Sglebius DPRINTF(1, ("%s: async connect pending, fd=%d\n", 1909290000Sglebius up->logname, up->fdt)); 1910290000Sglebius return; 1911290000Sglebius } 1912290000Sglebius 1913275970Scy if (syslogok(pp, up)) 1914275970Scy msyslog(LOG_ERR, 1915275970Scy "%s: cannot connect GPSD socket: %m", 1916290000Sglebius up->logname); 1917275970Scy goto no_socket; 1918275970Scy } 1919275970Scy 1920290000Sglebius /* We had a successful synchronous connect, so we add the 1921290000Sglebius * refclock processing ASAP. We still have to wait for the 1922290000Sglebius * version string and apply the watch command later on, but we 1923290000Sglebius * might as well get the show on the road now. 1924290000Sglebius */ 1925290000Sglebius DPRINTF(1, ("%s: new socket connection, fd=%d\n", 1926290000Sglebius up->logname, up->fdt)); 1927290000Sglebius 1928290000Sglebius pp->io.fd = up->fdt; 1929290000Sglebius up->fdt = -1; 1930290000Sglebius if (0 == io_addclock(&pp->io)) { 1931290000Sglebius if (syslogok(pp, up)) 1932290000Sglebius msyslog(LOG_ERR, 1933290000Sglebius "%s: failed to register with I/O engine", 1934290000Sglebius up->logname); 1935290000Sglebius goto no_socket; 1936290000Sglebius } 1937290000Sglebius 1938275970Scy return; 1939290000Sglebius 1940275970Scy no_socket: 1941290000Sglebius if (-1 != pp->io.fd) 1942290000Sglebius close(pp->io.fd); 1943275970Scy if (-1 != up->fdt) 1944275970Scy close(up->fdt); 1945290000Sglebius pp->io.fd = -1; 1946275970Scy up->fdt = -1; 1947275970Scy up->tickover = up->tickpres; 1948275970Scy up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH); 1949275970Scy} 1950275970Scy 1951275970Scy/* ------------------------------------------------------------------ */ 1952275970Scy 1953275970Scystatic void 1954275970Scygpsd_test_socket( 1955275970Scy peerT * const peer) 1956275970Scy{ 1957275970Scy clockprocT * const pp = peer->procptr; 1958275970Scy gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 1959275970Scy 1960275970Scy int ec, rc; 1961275970Scy socklen_t lc; 1962275970Scy 1963275970Scy /* Check if the non-blocking connect was finished by testing the 1964275970Scy * socket for writeability. Use the 'poll()' API if available 1965275970Scy * and 'select()' otherwise. 1966275970Scy */ 1967290000Sglebius DPRINTF(2, ("%s: check connect, fd=%d\n", 1968290000Sglebius up->logname, up->fdt)); 1969275970Scy 1970275970Scy#if defined(HAVE_SYS_POLL_H) 1971275970Scy { 1972275970Scy struct pollfd pfd; 1973275970Scy 1974275970Scy pfd.events = POLLOUT; 1975275970Scy pfd.fd = up->fdt; 1976275970Scy rc = poll(&pfd, 1, 0); 1977275970Scy if (1 != rc || !(pfd.revents & POLLOUT)) 1978275970Scy return; 1979275970Scy } 1980275970Scy#elif defined(HAVE_SYS_SELECT_H) 1981275970Scy { 1982275970Scy struct timeval tout; 1983275970Scy fd_set wset; 1984275970Scy 1985275970Scy memset(&tout, 0, sizeof(tout)); 1986275970Scy FD_ZERO(&wset); 1987275970Scy FD_SET(up->fdt, &wset); 1988275970Scy rc = select(up->fdt+1, NULL, &wset, NULL, &tout); 1989275970Scy if (0 == rc || !(FD_ISSET(up->fdt, &wset))) 1990275970Scy return; 1991275970Scy } 1992275970Scy#else 1993275970Scy# error Blooper! That should have been found earlier! 1994275970Scy#endif 1995275970Scy 1996275970Scy /* next timeout is a full one... */ 1997275970Scy up->tickover = TICKOVER_LOW; 1998275970Scy 1999275970Scy /* check for socket error */ 2000275970Scy ec = 0; 2001275970Scy lc = sizeof(ec); 2002275970Scy rc = getsockopt(up->fdt, SOL_SOCKET, SO_ERROR, &ec, &lc); 2003275970Scy if (-1 == rc || 0 != ec) { 2004290000Sglebius const char *errtxt; 2005290000Sglebius if (0 == ec) 2006290000Sglebius ec = errno; 2007290000Sglebius errtxt = strerror(ec); 2008275970Scy if (syslogok(pp, up)) 2009275970Scy msyslog(LOG_ERR, 2010290000Sglebius "%s: async connect to GPSD failed," 2011290000Sglebius " fd=%d, ec=%d(%s)", 2012290000Sglebius up->logname, up->fdt, ec, errtxt); 2013290000Sglebius else 2014290000Sglebius DPRINTF(1, ("%s: async connect to GPSD failed," 2015290000Sglebius " fd=%d, ec=%d(%s)\n", 2016290000Sglebius up->logname, up->fdt, ec, errtxt)); 2017275970Scy goto no_socket; 2018290000Sglebius } else { 2019290000Sglebius DPRINTF(1, ("%s: async connect to GPSD succeeded, fd=%d\n", 2020290000Sglebius up->logname, up->fdt)); 2021290000Sglebius } 2022290000Sglebius 2023275970Scy /* swap socket FDs, and make sure the clock was added */ 2024275970Scy pp->io.fd = up->fdt; 2025275970Scy up->fdt = -1; 2026275970Scy if (0 == io_addclock(&pp->io)) { 2027275970Scy if (syslogok(pp, up)) 2028275970Scy msyslog(LOG_ERR, 2029275970Scy "%s: failed to register with I/O engine", 2030290000Sglebius up->logname); 2031275970Scy goto no_socket; 2032275970Scy } 2033275970Scy return; 2034290000Sglebius 2035275970Scy no_socket: 2036290000Sglebius if (-1 != up->fdt) { 2037290000Sglebius DPRINTF(1, ("%s: closing socket, fd=%d\n", 2038290000Sglebius up->logname, up->fdt)); 2039275970Scy close(up->fdt); 2040290000Sglebius } 2041275970Scy up->fdt = -1; 2042275970Scy up->tickover = up->tickpres; 2043275970Scy up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH); 2044275970Scy} 2045275970Scy 2046275970Scy/* ===================================================================== 2047275970Scy * helper stuff 2048275970Scy */ 2049275970Scy 2050290000Sglebius/* ------------------------------------------------------------------- 2051290000Sglebius * store a properly clamped precision value 2052275970Scy */ 2053290000Sglebiusstatic int16_t 2054290000Sglebiusclamped_precision( 2055290000Sglebius int rawprec) 2056275970Scy{ 2057290000Sglebius if (rawprec > 0) 2058290000Sglebius rawprec = 0; 2059290000Sglebius if (rawprec < -32) 2060290000Sglebius rawprec = -32; 2061290000Sglebius return (int16_t)rawprec; 2062275970Scy} 2063275970Scy 2064275970Scy/* ------------------------------------------------------------------- 2065290000Sglebius * Convert a GPSD timestamp (ISO8601 Format) to an l_fp 2066275970Scy */ 2067275970Scystatic BOOL 2068275970Scyconvert_ascii_time( 2069275970Scy l_fp * fp , 2070275970Scy const char * gps_time) 2071275970Scy{ 2072275970Scy char *ep; 2073275970Scy struct tm gd; 2074275970Scy struct timespec ts; 2075290000Sglebius uint32_t dw; 2076275970Scy 2077275970Scy /* Use 'strptime' to take the brunt of the work, then parse 2078275970Scy * the fractional part manually, starting with a digit weight of 2079275970Scy * 10^8 nanoseconds. 2080275970Scy */ 2081275970Scy ts.tv_nsec = 0; 2082275970Scy ep = strptime(gps_time, "%Y-%m-%dT%H:%M:%S", &gd); 2083290000Sglebius if (NULL == ep) 2084290000Sglebius return FALSE; /* could not parse the mandatory stuff! */ 2085275970Scy if (*ep == '.') { 2086290000Sglebius dw = 100000000u; 2087290000Sglebius while (isdigit(*(u_char*)++ep)) { 2088290000Sglebius ts.tv_nsec += (*(u_char*)ep - '0') * dw; 2089290000Sglebius dw /= 10u; 2090275970Scy } 2091275970Scy } 2092275970Scy if (ep[0] != 'Z' || ep[1] != '\0') 2093290000Sglebius return FALSE; /* trailing garbage */ 2094275970Scy 2095290000Sglebius /* Now convert the whole thing into a 'l_fp'. We do not use 2096290000Sglebius * 'mkgmtime()' since its not standard and going through the 2097290000Sglebius * calendar routines is not much effort, either. 2098290000Sglebius */ 2099275970Scy ts.tv_sec = (ntpcal_tm_to_rd(&gd) - DAY_NTP_STARTS) * SECSPERDAY 2100275970Scy + ntpcal_tm_to_daysec(&gd); 2101275970Scy *fp = tspec_intv_to_lfp(ts); 2102275970Scy 2103275970Scy return TRUE; 2104275970Scy} 2105275970Scy 2106275970Scy/* ------------------------------------------------------------------- 2107275970Scy * Save the last timecode string, making sure it's properly truncated 2108275970Scy * if necessary and NUL terminated in any case. 2109275970Scy */ 2110275970Scystatic void 2111275970Scysave_ltc( 2112275970Scy clockprocT * const pp, 2113275970Scy const char * const tc) 2114275970Scy{ 2115275970Scy size_t len; 2116275970Scy 2117275970Scy len = (tc) ? strlen(tc) : 0; 2118275970Scy if (len >= sizeof(pp->a_lastcode)) 2119275970Scy len = sizeof(pp->a_lastcode) - 1; 2120275970Scy pp->lencode = (u_short)len; 2121275970Scy memcpy(pp->a_lastcode, tc, len); 2122275970Scy pp->a_lastcode[len] = '\0'; 2123275970Scy} 2124275970Scy 2125290000Sglebius/* ------------------------------------------------------------------- 2126275970Scy * asprintf replacement... it's not available everywhere... 2127275970Scy */ 2128275970Scystatic int 2129275970Scymyasprintf( 2130275970Scy char ** spp, 2131275970Scy char const * fmt, 2132275970Scy ... ) 2133275970Scy{ 2134275970Scy size_t alen, plen; 2135275970Scy 2136275970Scy alen = 32; 2137275970Scy *spp = NULL; 2138275970Scy do { 2139275970Scy va_list va; 2140275970Scy 2141275970Scy alen += alen; 2142275970Scy free(*spp); 2143275970Scy *spp = (char*)malloc(alen); 2144275970Scy if (NULL == *spp) 2145275970Scy return -1; 2146275970Scy 2147275970Scy va_start(va, fmt); 2148275970Scy plen = (size_t)vsnprintf(*spp, alen, fmt, va); 2149275970Scy va_end(va); 2150275970Scy } while (plen >= alen); 2151275970Scy 2152275970Scy return (int)plen; 2153275970Scy} 2154275970Scy 2155290000Sglebius/* ------------------------------------------------------------------- 2156290000Sglebius * dump a raw data buffer 2157290000Sglebius */ 2158290000Sglebius 2159290000Sglebiusstatic char * 2160290000Sglebiusadd_string( 2161290000Sglebius char *dp, 2162290000Sglebius char *ep, 2163290000Sglebius const char *sp) 2164290000Sglebius{ 2165290000Sglebius while (dp != ep && *sp) 2166290000Sglebius *dp++ = *sp++; 2167290000Sglebius return dp; 2168290000Sglebius} 2169290000Sglebius 2170290000Sglebiusstatic void 2171290000Sglebiuslog_data( 2172290000Sglebius peerT *peer, 2173290000Sglebius const char *what, 2174290000Sglebius const char *buf , 2175290000Sglebius size_t len ) 2176290000Sglebius{ 2177290000Sglebius /* we're running single threaded with regards to the clocks. */ 2178290000Sglebius static char s_lbuf[2048]; 2179290000Sglebius 2180290000Sglebius clockprocT * const pp = peer->procptr; 2181290000Sglebius gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; 2182290000Sglebius 2183290000Sglebius if (debug > 1) { 2184290000Sglebius const char *sptr = buf; 2185290000Sglebius const char *stop = buf + len; 2186290000Sglebius char *dptr = s_lbuf; 2187290000Sglebius char *dtop = s_lbuf + sizeof(s_lbuf) - 1; /* for NUL */ 2188290000Sglebius 2189290000Sglebius while (sptr != stop && dptr != dtop) { 2190290000Sglebius u_char uch = (u_char)*sptr++; 2191290000Sglebius if (uch == '\\') { 2192290000Sglebius dptr = add_string(dptr, dtop, "\\\\"); 2193290000Sglebius } else if (isprint(uch)) { 2194290000Sglebius *dptr++ = (char)uch; 2195290000Sglebius } else { 2196290000Sglebius char fbuf[6]; 2197290000Sglebius snprintf(fbuf, sizeof(fbuf), "\\%03o", uch); 2198290000Sglebius dptr = add_string(dptr, dtop, fbuf); 2199290000Sglebius } 2200290000Sglebius } 2201290000Sglebius *dptr = '\0'; 2202290000Sglebius mprintf("%s[%s]: '%s'\n", up->logname, what, s_lbuf); 2203290000Sglebius } 2204290000Sglebius} 2205290000Sglebius 2206275970Scy#else 2207275970ScyNONEMPTY_TRANSLATION_UNIT 2208275970Scy#endif /* REFCLOCK && CLOCK_GPSDJSON */ 2209