12b15cb3dSCy Schubert /*
22b15cb3dSCy Schubert * refclock_gpsdjson.c - clock driver as GPSD JSON client
32b15cb3dSCy Schubert * Juergen Perlinger (perlinger@ntp.org)
42b15cb3dSCy Schubert * Feb 11, 2014 for the NTP project.
52b15cb3dSCy Schubert * The contents of 'html/copyright.html' apply.
62b15cb3dSCy Schubert *
72b15cb3dSCy Schubert * Heavily inspired by refclock_nmea.c
82b15cb3dSCy Schubert *
9276da39aSCy Schubert * Special thanks to Gary Miller and Hal Murray for their comments and
10276da39aSCy Schubert * ideas.
11276da39aSCy Schubert *
122b15cb3dSCy Schubert * Note: This will currently NOT work with Windows due to some
132b15cb3dSCy Schubert * limitations:
142b15cb3dSCy Schubert *
152b15cb3dSCy Schubert * - There is no GPSD for Windows. (There is an unofficial port to
162b15cb3dSCy Schubert * cygwin, but Windows is not officially supported.)
172b15cb3dSCy Schubert *
18276da39aSCy Schubert * - To work properly, this driver needs PPS and TPV/TOFF sentences
19276da39aSCy Schubert * from GPSD. I don't see how the cygwin port should deal with the
20276da39aSCy Schubert * PPS signal.
212b15cb3dSCy Schubert *
222b15cb3dSCy Schubert * - The device name matching must be done in a different way for
232b15cb3dSCy Schubert * Windows. (Can be done with COMxx matching, as done for NMEA.)
242b15cb3dSCy Schubert *
252b15cb3dSCy Schubert * Apart from those minor hickups, once GPSD has been fully ported to
26276da39aSCy Schubert * Windows, there's no reason why this should not work there ;-) If this
27276da39aSCy Schubert * is ever to happen at all is a different question.
28276da39aSCy Schubert *
29276da39aSCy Schubert * ---------------------------------------------------------------------
30276da39aSCy Schubert *
31276da39aSCy Schubert * This driver works slightly different from most others, as the PPS
32276da39aSCy Schubert * information (if available) is also coming from GPSD via the data
33276da39aSCy Schubert * connection. This makes using both the PPS data and the serial data
34276da39aSCy Schubert * easier, but OTOH it's not possible to use the ATOM driver to feed a
35276da39aSCy Schubert * raw PPS stream to the core of NTPD.
36276da39aSCy Schubert *
37276da39aSCy Schubert * To go around this, the driver can use a secondary clock unit
38276da39aSCy Schubert * (units>=128) that operate in tandem with the primary clock unit
39276da39aSCy Schubert * (unit%128). The primary clock unit does all the IO stuff and data
40276da39aSCy Schubert * decoding; if a a secondary unit is attached to a primary unit, this
41276da39aSCy Schubert * secondary unit is feed with the PPS samples only and can act as a PPS
42276da39aSCy Schubert * source to the clock selection.
43276da39aSCy Schubert *
44276da39aSCy Schubert * The drawback is that the primary unit must be present for the
45276da39aSCy Schubert * secondary unit to work.
46276da39aSCy Schubert *
47276da39aSCy Schubert * This design is a compromise to reduce the IO load for both NTPD and
48276da39aSCy Schubert * GPSD; it also ensures that data is transmitted and evaluated only
49276da39aSCy Schubert * once on the side of NTPD.
50276da39aSCy Schubert *
51276da39aSCy Schubert * ---------------------------------------------------------------------
52276da39aSCy Schubert *
53276da39aSCy Schubert * trouble shooting hints:
54276da39aSCy Schubert *
55276da39aSCy Schubert * Enable and check the clock stats. Check if there are bad replies;
56276da39aSCy Schubert * there should be none. If there are actually bad replies, then the
57276da39aSCy Schubert * driver cannot parse all JSON records from GPSD, and some record
58276da39aSCy Schubert * types are vital for the operation of the driver. This indicates a
59276da39aSCy Schubert * problem on the protocol level.
60276da39aSCy Schubert *
61276da39aSCy Schubert * When started on the command line with a debug level >= 2, the
62276da39aSCy Schubert * driver dumps the raw received data and the parser input to
63276da39aSCy Schubert * stdout. Since the debug level is global, NTPD starts to create a
64276da39aSCy Schubert * *lot* of output. It makes sense to pipe it through '(f)grep
65276da39aSCy Schubert * GPSD_JSON' before writing the result to disk.
66276da39aSCy Schubert *
67276da39aSCy Schubert * A bit less intrusive is using netcat or telnet to connect to GPSD
68276da39aSCy Schubert * and snoop what NTPD would get. If you try this, you have to send a
69276da39aSCy Schubert * WATCH command to GPSD:
70276da39aSCy Schubert *
71276da39aSCy Schubert * ?WATCH={"device":"/dev/gps0","enable":true,"json":true,"pps":true};<CRLF>
72276da39aSCy Schubert *
73276da39aSCy Schubert * should show you what GPSD has to say to NTPD. Replace "/dev/gps0"
74276da39aSCy Schubert * with the device link used by GPSD, if necessary.
752b15cb3dSCy Schubert */
762b15cb3dSCy Schubert
77276da39aSCy Schubert
782b15cb3dSCy Schubert #ifdef HAVE_CONFIG_H
792b15cb3dSCy Schubert #include <config.h>
802b15cb3dSCy Schubert #endif
812b15cb3dSCy Schubert
822b15cb3dSCy Schubert #include "ntp_types.h"
832b15cb3dSCy Schubert
842b15cb3dSCy Schubert #if defined(REFCLOCK) && defined(CLOCK_GPSDJSON) && !defined(SYS_WINNT)
852b15cb3dSCy Schubert
862b15cb3dSCy Schubert /* =====================================================================
87276da39aSCy Schubert * Get the little JSMN library directly into our guts. Use the 'parent
88276da39aSCy Schubert * link' feature for maximum speed.
892b15cb3dSCy Schubert */
90276da39aSCy Schubert #define JSMN_PARENT_LINKS
912b15cb3dSCy Schubert #include "../libjsmn/jsmn.c"
922b15cb3dSCy Schubert
932b15cb3dSCy Schubert /* =====================================================================
94276da39aSCy Schubert * JSON parsing stuff
95276da39aSCy Schubert */
96276da39aSCy Schubert
97276da39aSCy Schubert #define JSMN_MAXTOK 350
98276da39aSCy Schubert #define INVALID_TOKEN (-1)
99276da39aSCy Schubert
100276da39aSCy Schubert typedef struct json_ctx {
101276da39aSCy Schubert char * buf;
102276da39aSCy Schubert int ntok;
103276da39aSCy Schubert jsmntok_t tok[JSMN_MAXTOK];
104276da39aSCy Schubert } json_ctx;
105276da39aSCy Schubert
106276da39aSCy Schubert typedef int tok_ref;
107276da39aSCy Schubert
108276da39aSCy Schubert /* Not all targets have 'long long', and not all of them have 'strtoll'.
109276da39aSCy Schubert * Sigh. We roll our own integer number parser.
110276da39aSCy Schubert */
111276da39aSCy Schubert #ifdef HAVE_LONG_LONG
112276da39aSCy Schubert typedef signed long long int json_int;
113276da39aSCy Schubert typedef unsigned long long int json_uint;
114276da39aSCy Schubert #define JSON_INT_MAX LLONG_MAX
115276da39aSCy Schubert #define JSON_INT_MIN LLONG_MIN
116276da39aSCy Schubert #else
117276da39aSCy Schubert typedef signed long int json_int;
118276da39aSCy Schubert typedef unsigned long int json_uint;
119276da39aSCy Schubert #define JSON_INT_MAX LONG_MAX
120276da39aSCy Schubert #define JSON_INT_MIN LONG_MIN
121276da39aSCy Schubert #endif
122276da39aSCy Schubert
123276da39aSCy Schubert /* =====================================================================
1242b15cb3dSCy Schubert * header stuff we need
1252b15cb3dSCy Schubert */
1262b15cb3dSCy Schubert
1272b15cb3dSCy Schubert #include <netdb.h>
1282b15cb3dSCy Schubert #include <unistd.h>
1292b15cb3dSCy Schubert #include <fcntl.h>
1302b15cb3dSCy Schubert #include <string.h>
1312b15cb3dSCy Schubert #include <ctype.h>
132276da39aSCy Schubert #include <math.h>
1332b15cb3dSCy Schubert
1342b15cb3dSCy Schubert #include <sys/types.h>
1352b15cb3dSCy Schubert #include <sys/socket.h>
1362b15cb3dSCy Schubert #include <sys/stat.h>
1372b15cb3dSCy Schubert #include <netinet/tcp.h>
1382b15cb3dSCy Schubert
1392b15cb3dSCy Schubert #if defined(HAVE_SYS_POLL_H)
1402b15cb3dSCy Schubert # include <sys/poll.h>
1412b15cb3dSCy Schubert #elif defined(HAVE_SYS_SELECT_H)
1422b15cb3dSCy Schubert # include <sys/select.h>
1432b15cb3dSCy Schubert #else
1442b15cb3dSCy Schubert # error need poll() or select()
1452b15cb3dSCy Schubert #endif
1462b15cb3dSCy Schubert
1472b15cb3dSCy Schubert #include "ntpd.h"
1482b15cb3dSCy Schubert #include "ntp_io.h"
1492b15cb3dSCy Schubert #include "ntp_unixtime.h"
1502b15cb3dSCy Schubert #include "ntp_refclock.h"
1512b15cb3dSCy Schubert #include "ntp_stdlib.h"
1522b15cb3dSCy Schubert #include "ntp_calendar.h"
153*a466cc55SCy Schubert #include "ntp_clockdev.h"
1542b15cb3dSCy Schubert #include "timespecops.h"
1552b15cb3dSCy Schubert
156276da39aSCy Schubert /* get operation modes from mode word.
157276da39aSCy Schubert
158276da39aSCy Schubert * + SERIAL (default) evaluates only serial time information ('STI') as
159276da39aSCy Schubert * provided by TPV and TOFF records. TPV evaluation suffers from a
160276da39aSCy Schubert * bigger jitter than TOFF, sine it does not contain the receive time
161276da39aSCy Schubert * from GPSD and therefore the receive time of NTPD must be
162276da39aSCy Schubert * substituted for it. The network latency makes this a second rate
163276da39aSCy Schubert * guess.
164276da39aSCy Schubert *
165276da39aSCy Schubert * If TOFF records are detected in the data stream, the timing
166276da39aSCy Schubert * information is gleaned from this record -- it contains the local
167276da39aSCy Schubert * receive time stamp from GPSD and therefore eliminates the
168276da39aSCy Schubert * transmission latency between GPSD and NTPD. The timing information
169276da39aSCy Schubert * from TPV is ignored once a TOFF is detected or expected.
170276da39aSCy Schubert *
171276da39aSCy Schubert * TPV is still used to check the fix status, so the driver can stop
172276da39aSCy Schubert * feeding samples when GPSD says that the time information is
173276da39aSCy Schubert * effectively unreliable.
174276da39aSCy Schubert *
175276da39aSCy Schubert * + STRICT means only feed clock samples when a valid STI/PPS pair is
176276da39aSCy Schubert * available. Combines the reference time from STI with the pulse time
177276da39aSCy Schubert * from PPS. Masks the serial data jitter as long PPS is available,
178276da39aSCy Schubert * but can rapidly deteriorate once PPS drops out.
179276da39aSCy Schubert *
180276da39aSCy Schubert * + AUTO tries to use STI/PPS pairs if available for some time, and if
181276da39aSCy Schubert * this fails for too long switches back to STI only until the PPS
182276da39aSCy Schubert * signal becomes available again. See the HTML docs for this driver
183276da39aSCy Schubert * about the gotchas and why this is not the default.
184276da39aSCy Schubert */
185276da39aSCy Schubert #define MODE_OP_MASK 0x03
186276da39aSCy Schubert #define MODE_OP_STI 0
187276da39aSCy Schubert #define MODE_OP_STRICT 1
188276da39aSCy Schubert #define MODE_OP_AUTO 2
189276da39aSCy Schubert #define MODE_OP_MAXVAL 2
190276da39aSCy Schubert #define MODE_OP_MODE(x) ((x) & MODE_OP_MASK)
191276da39aSCy Schubert
1922b15cb3dSCy Schubert #define PRECISION (-9) /* precision assumed (about 2 ms) */
1932b15cb3dSCy Schubert #define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
1942b15cb3dSCy Schubert #define REFID "GPSD" /* reference id */
1952b15cb3dSCy Schubert #define DESCRIPTION "GPSD JSON client clock" /* who we are */
1962b15cb3dSCy Schubert
197*a466cc55SCy Schubert #define MAX_PDU_LEN 8192 /* multi-GNSS reports can be HUGE */
1982b15cb3dSCy Schubert #define TICKOVER_LOW 10
1992b15cb3dSCy Schubert #define TICKOVER_HIGH 120
2002b15cb3dSCy Schubert #define LOGTHROTTLE 3600
2012b15cb3dSCy Schubert
202276da39aSCy Schubert /* Primary channel PPS avilability dance:
203276da39aSCy Schubert * Every good PPS sample gets us a credit of PPS_INCCOUNT points, every
204276da39aSCy Schubert * bad/missing PPS sample costs us a debit of PPS_DECCOUNT points. When
205276da39aSCy Schubert * the account reaches the upper limit we change to a mode where only
206276da39aSCy Schubert * PPS-augmented samples are fed to the core; when the account drops to
207276da39aSCy Schubert * zero we switch to a mode where TPV-only timestamps are fed to the
208276da39aSCy Schubert * core.
209276da39aSCy Schubert * This reduces the chance of rapid alternation between raw and
210276da39aSCy Schubert * PPS-augmented time stamps.
211276da39aSCy Schubert */
212276da39aSCy Schubert #define PPS_MAXCOUNT 60 /* upper limit of account */
213276da39aSCy Schubert #define PPS_INCCOUNT 3 /* credit for good samples */
214276da39aSCy Schubert #define PPS_DECCOUNT 1 /* debit for bad samples */
215276da39aSCy Schubert
216276da39aSCy Schubert /* The secondary (PPS) channel uses a different strategy to avoid old
217276da39aSCy Schubert * PPS samples in the median filter.
218276da39aSCy Schubert */
219276da39aSCy Schubert #define PPS2_MAXCOUNT 10
2202b15cb3dSCy Schubert
2212b15cb3dSCy Schubert #ifndef BOOL
2222b15cb3dSCy Schubert # define BOOL int
2232b15cb3dSCy Schubert #endif
2242b15cb3dSCy Schubert #ifndef TRUE
2252b15cb3dSCy Schubert # define TRUE 1
2262b15cb3dSCy Schubert #endif
2272b15cb3dSCy Schubert #ifndef FALSE
2282b15cb3dSCy Schubert # define FALSE 0
2292b15cb3dSCy Schubert #endif
2302b15cb3dSCy Schubert
231276da39aSCy Schubert #define PROTO_VERSION(hi,lo) \
232276da39aSCy Schubert ((((uint32_t)(hi) << 16) & 0xFFFF0000u) | \
233276da39aSCy Schubert ((uint32_t)(lo) & 0x0FFFFu))
234276da39aSCy Schubert
2352b15cb3dSCy Schubert /* some local typedefs: The NTPD formatting style cries for short type
2362b15cb3dSCy Schubert * names, and we provide them locally. Note:the suffix '_t' is reserved
2372b15cb3dSCy Schubert * for the standard; I use a capital T instead.
2382b15cb3dSCy Schubert */
2392b15cb3dSCy Schubert typedef struct peer peerT;
2402b15cb3dSCy Schubert typedef struct refclockproc clockprocT;
2412b15cb3dSCy Schubert typedef struct addrinfo addrinfoT;
2422b15cb3dSCy Schubert
2432b15cb3dSCy Schubert /* =====================================================================
2442b15cb3dSCy Schubert * We use the same device name scheme as does the NMEA driver; since
2452b15cb3dSCy Schubert * GPSD supports the same links, we can select devices by a fixed name.
2462b15cb3dSCy Schubert */
2472b15cb3dSCy Schubert static const char * s_dev_stem = "/dev/gps";
2482b15cb3dSCy Schubert
2492b15cb3dSCy Schubert /* =====================================================================
2502b15cb3dSCy Schubert * forward declarations for transfer vector and the vector itself
2512b15cb3dSCy Schubert */
2522b15cb3dSCy Schubert
2532b15cb3dSCy Schubert static void gpsd_init (void);
2542b15cb3dSCy Schubert static int gpsd_start (int, peerT *);
2552b15cb3dSCy Schubert static void gpsd_shutdown (int, peerT *);
2562b15cb3dSCy Schubert static void gpsd_receive (struct recvbuf *);
2572b15cb3dSCy Schubert static void gpsd_poll (int, peerT *);
2582b15cb3dSCy Schubert static void gpsd_control (int, const struct refclockstat *,
2592b15cb3dSCy Schubert struct refclockstat *, peerT *);
2602b15cb3dSCy Schubert static void gpsd_timer (int, peerT *);
2612b15cb3dSCy Schubert
262276da39aSCy Schubert static int myasprintf(char**, char const*, ...) NTP_PRINTF(2, 3);
263276da39aSCy Schubert
264276da39aSCy Schubert static void enter_opmode(peerT *peer, int mode);
265276da39aSCy Schubert static void leave_opmode(peerT *peer, int mode);
2662b15cb3dSCy Schubert
2672b15cb3dSCy Schubert struct refclock refclock_gpsdjson = {
2682b15cb3dSCy Schubert gpsd_start, /* start up driver */
2692b15cb3dSCy Schubert gpsd_shutdown, /* shut down driver */
2702b15cb3dSCy Schubert gpsd_poll, /* transmit poll message */
2712b15cb3dSCy Schubert gpsd_control, /* fudge control */
2722b15cb3dSCy Schubert gpsd_init, /* initialize driver */
2732b15cb3dSCy Schubert noentry, /* buginfo */
2742b15cb3dSCy Schubert gpsd_timer /* called once per second */
2752b15cb3dSCy Schubert };
2762b15cb3dSCy Schubert
2772b15cb3dSCy Schubert /* =====================================================================
2782b15cb3dSCy Schubert * our local clock unit and data
2792b15cb3dSCy Schubert */
280276da39aSCy Schubert struct gpsd_unit;
281276da39aSCy Schubert typedef struct gpsd_unit gpsd_unitT;
2822b15cb3dSCy Schubert
283276da39aSCy Schubert struct gpsd_unit {
284276da39aSCy Schubert /* links for sharing between master/slave units */
285276da39aSCy Schubert gpsd_unitT *next_unit;
286276da39aSCy Schubert size_t refcount;
287276da39aSCy Schubert
288276da39aSCy Schubert /* data for the secondary PPS channel */
289276da39aSCy Schubert peerT *pps_peer;
290276da39aSCy Schubert
291276da39aSCy Schubert /* unit and operation modes */
292276da39aSCy Schubert int unit;
293276da39aSCy Schubert int mode;
294276da39aSCy Schubert char *logname; /* cached name for log/print */
295276da39aSCy Schubert char * device; /* device name of unit */
296276da39aSCy Schubert
297276da39aSCy Schubert /* current line protocol version */
298276da39aSCy Schubert uint32_t proto_version;
299276da39aSCy Schubert
300276da39aSCy Schubert /* PPS time stamps primary + secondary channel */
3012b15cb3dSCy Schubert l_fp pps_local; /* when we received the PPS message */
3022b15cb3dSCy Schubert l_fp pps_stamp; /* related reference time */
3032b15cb3dSCy Schubert l_fp pps_recvt; /* when GPSD detected the pulse */
304276da39aSCy Schubert l_fp pps_stamp2;/* related reference time (secondary) */
305276da39aSCy Schubert l_fp pps_recvt2;/* when GPSD detected the pulse (secondary)*/
306276da39aSCy Schubert int ppscount; /* PPS counter (primary unit) */
307276da39aSCy Schubert int ppscount2; /* PPS counter (secondary unit) */
3082b15cb3dSCy Schubert
309276da39aSCy Schubert /* TPV or TOFF serial time information */
310276da39aSCy Schubert l_fp sti_local; /* when we received the TPV/TOFF message */
311276da39aSCy Schubert l_fp sti_stamp; /* effective GPS time stamp */
312276da39aSCy Schubert l_fp sti_recvt; /* when GPSD got the fix */
313276da39aSCy Schubert
314276da39aSCy Schubert /* precision estimates */
315276da39aSCy Schubert int16_t sti_prec; /* serial precision based on EPT */
316276da39aSCy Schubert int16_t pps_prec; /* PPS precision from GPSD or above */
3172b15cb3dSCy Schubert
3182b15cb3dSCy Schubert /* fudge values for correction, mirrored as 'l_fp' */
319276da39aSCy Schubert l_fp pps_fudge; /* PPS fudge primary channel */
320276da39aSCy Schubert l_fp pps_fudge2; /* PPS fudge secondary channel */
321276da39aSCy Schubert l_fp sti_fudge; /* TPV/TOFF serial data fudge */
3222b15cb3dSCy Schubert
3232b15cb3dSCy Schubert /* Flags to indicate available data */
324276da39aSCy Schubert int fl_nosync: 1; /* GPSD signals bad quality */
325276da39aSCy Schubert int fl_sti : 1; /* valid TPV/TOFF seen (have time) */
3262b15cb3dSCy Schubert int fl_pps : 1; /* valid pulse seen */
327276da39aSCy Schubert int fl_pps2 : 1; /* valid pulse seen for PPS channel */
328276da39aSCy Schubert int fl_rawsti: 1; /* permit raw TPV/TOFF time stamps */
3292b15cb3dSCy Schubert int fl_vers : 1; /* have protocol version */
3302b15cb3dSCy Schubert int fl_watch : 1; /* watch reply seen */
331276da39aSCy Schubert /* protocol flags */
332276da39aSCy Schubert int pf_nsec : 1; /* have nanosec PPS info */
333276da39aSCy Schubert int pf_toff : 1; /* have TOFF record for timing */
3342b15cb3dSCy Schubert
3352b15cb3dSCy Schubert /* admin stuff for sockets and device selection */
3362b15cb3dSCy Schubert int fdt; /* current connecting socket */
3372b15cb3dSCy Schubert addrinfoT * addr; /* next address to try */
3382b15cb3dSCy Schubert u_int tickover; /* timeout countdown */
3392b15cb3dSCy Schubert u_int tickpres; /* timeout preset */
3402b15cb3dSCy Schubert
3412b15cb3dSCy Schubert /* tallies for the various events */
3422b15cb3dSCy Schubert u_int tc_recv; /* received known records */
343276da39aSCy Schubert u_int tc_breply; /* bad replies / parsing errors */
344276da39aSCy Schubert u_int tc_nosync; /* TPV / sample cycles w/o fix */
345276da39aSCy Schubert u_int tc_sti_recv;/* received serial time info records */
346276da39aSCy Schubert u_int tc_sti_used;/* used --^-- */
347276da39aSCy Schubert u_int tc_pps_recv;/* received PPS timing info records */
348276da39aSCy Schubert u_int tc_pps_used;/* used --^-- */
3492b15cb3dSCy Schubert
3502b15cb3dSCy Schubert /* log bloat throttle */
3512b15cb3dSCy Schubert u_int logthrottle;/* seconds to next log slot */
3522b15cb3dSCy Schubert
353276da39aSCy Schubert /* The parse context for the current record */
354276da39aSCy Schubert json_ctx json_parse;
355276da39aSCy Schubert
3562b15cb3dSCy Schubert /* record assemby buffer and saved length */
3572b15cb3dSCy Schubert int buflen;
3582b15cb3dSCy Schubert char buffer[MAX_PDU_LEN];
359276da39aSCy Schubert };
3602b15cb3dSCy Schubert
3612b15cb3dSCy Schubert /* =====================================================================
3622b15cb3dSCy Schubert * static local helpers forward decls
3632b15cb3dSCy Schubert */
3642b15cb3dSCy Schubert static void gpsd_init_socket(peerT * const peer);
3652b15cb3dSCy Schubert static void gpsd_test_socket(peerT * const peer);
3662b15cb3dSCy Schubert static void gpsd_stop_socket(peerT * const peer);
3672b15cb3dSCy Schubert
3682b15cb3dSCy Schubert static void gpsd_parse(peerT * const peer,
3692b15cb3dSCy Schubert const l_fp * const rtime);
3702b15cb3dSCy Schubert static BOOL convert_ascii_time(l_fp * fp, const char * gps_time);
3712b15cb3dSCy Schubert static void save_ltc(clockprocT * const pp, const char * const tc);
3722b15cb3dSCy Schubert static int syslogok(clockprocT * const pp, gpsd_unitT * const up);
373*a466cc55SCy Schubert static void log_data(peerT *peer, int level, const char *what,
374276da39aSCy Schubert const char *buf, size_t len);
375276da39aSCy Schubert static int16_t clamped_precision(int rawprec);
3762b15cb3dSCy Schubert
3772b15cb3dSCy Schubert /* =====================================================================
3782b15cb3dSCy Schubert * local / static stuff
3792b15cb3dSCy Schubert */
3802b15cb3dSCy Schubert
381276da39aSCy Schubert static const char * const s_req_version =
382276da39aSCy Schubert "?VERSION;\r\n";
383276da39aSCy Schubert
384276da39aSCy Schubert /* We keep a static list of network addresses for 'localhost:gpsd' or a
385276da39aSCy Schubert * fallback alias of it, and we try to connect to them in round-robin
386276da39aSCy Schubert * fashion. The service lookup is done during the driver init
387276da39aSCy Schubert * function to minmise the impact of 'getaddrinfo()'.
388276da39aSCy Schubert *
389276da39aSCy Schubert * Alas, the init function is called even if there are no clocks
390276da39aSCy Schubert * configured for this driver. So it makes sense to defer the logging of
391276da39aSCy Schubert * any errors or other notifications until the first clock unit is
392276da39aSCy Schubert * started -- otherwise there might be syslog entries from a driver that
393276da39aSCy Schubert * is not used at all.
3942b15cb3dSCy Schubert */
3952b15cb3dSCy Schubert static addrinfoT *s_gpsd_addr;
396276da39aSCy Schubert static gpsd_unitT *s_clock_units;
397276da39aSCy Schubert
398276da39aSCy Schubert /* list of service/socket names we want to resolve against */
399276da39aSCy Schubert static const char * const s_svctab[][2] = {
400276da39aSCy Schubert { "localhost", "gpsd" },
401276da39aSCy Schubert { "localhost", "2947" },
402276da39aSCy Schubert { "127.0.0.1", "2947" },
403276da39aSCy Schubert { NULL, NULL }
404276da39aSCy Schubert };
405276da39aSCy Schubert
406276da39aSCy Schubert /* list of address resolution errors and index of service entry that
407276da39aSCy Schubert * finally worked.
408276da39aSCy Schubert */
409276da39aSCy Schubert static int s_svcerr[sizeof(s_svctab)/sizeof(s_svctab[0])];
410276da39aSCy Schubert static int s_svcidx;
4112b15cb3dSCy Schubert
4122b15cb3dSCy Schubert /* =====================================================================
4132b15cb3dSCy Schubert * log throttling
4142b15cb3dSCy Schubert */
4152b15cb3dSCy Schubert static int/*BOOL*/
syslogok(clockprocT * const pp,gpsd_unitT * const up)4162b15cb3dSCy Schubert syslogok(
4172b15cb3dSCy Schubert clockprocT * const pp,
4182b15cb3dSCy Schubert gpsd_unitT * const up)
4192b15cb3dSCy Schubert {
4202b15cb3dSCy Schubert int res = (0 != (pp->sloppyclockflag & CLK_FLAG3))
4212b15cb3dSCy Schubert || (0 == up->logthrottle )
4222b15cb3dSCy Schubert || (LOGTHROTTLE == up->logthrottle );
4232b15cb3dSCy Schubert if (res)
4242b15cb3dSCy Schubert up->logthrottle = LOGTHROTTLE;
4252b15cb3dSCy Schubert return res;
4262b15cb3dSCy Schubert }
4272b15cb3dSCy Schubert
4282b15cb3dSCy Schubert /* =====================================================================
4292b15cb3dSCy Schubert * the clock functions
4302b15cb3dSCy Schubert */
4312b15cb3dSCy Schubert
4322b15cb3dSCy Schubert /* ---------------------------------------------------------------------
4332b15cb3dSCy Schubert * Init: This currently just gets the socket address for the GPS daemon
4342b15cb3dSCy Schubert */
4352b15cb3dSCy Schubert static void
gpsd_init(void)4362b15cb3dSCy Schubert gpsd_init(void)
4372b15cb3dSCy Schubert {
4382b15cb3dSCy Schubert addrinfoT hints;
439276da39aSCy Schubert int rc, idx;
4402b15cb3dSCy Schubert
441276da39aSCy Schubert memset(s_svcerr, 0, sizeof(s_svcerr));
4422b15cb3dSCy Schubert memset(&hints, 0, sizeof(hints));
4432b15cb3dSCy Schubert hints.ai_family = AF_UNSPEC;
4442b15cb3dSCy Schubert hints.ai_protocol = IPPROTO_TCP;
4452b15cb3dSCy Schubert hints.ai_socktype = SOCK_STREAM;
4462b15cb3dSCy Schubert
447276da39aSCy Schubert for (idx = 0; s_svctab[idx][0] && !s_gpsd_addr; idx++) {
448276da39aSCy Schubert rc = getaddrinfo(s_svctab[idx][0], s_svctab[idx][1],
449276da39aSCy Schubert &hints, &s_gpsd_addr);
450276da39aSCy Schubert s_svcerr[idx] = rc;
451276da39aSCy Schubert if (0 == rc)
452276da39aSCy Schubert break;
4532b15cb3dSCy Schubert s_gpsd_addr = NULL;
4542b15cb3dSCy Schubert }
455276da39aSCy Schubert s_svcidx = idx;
456276da39aSCy Schubert }
457276da39aSCy Schubert
458276da39aSCy Schubert /* ---------------------------------------------------------------------
459276da39aSCy Schubert * Init Check: flush pending log messages and check if we can proceed
460276da39aSCy Schubert */
461276da39aSCy Schubert static int/*BOOL*/
gpsd_init_check(void)462276da39aSCy Schubert gpsd_init_check(void)
463276da39aSCy Schubert {
464276da39aSCy Schubert int idx;
465276da39aSCy Schubert
466276da39aSCy Schubert /* Check if there is something to log */
467276da39aSCy Schubert if (s_svcidx == 0)
468276da39aSCy Schubert return (s_gpsd_addr != NULL);
469276da39aSCy Schubert
470276da39aSCy Schubert /* spool out the resolver errors */
471276da39aSCy Schubert for (idx = 0; idx < s_svcidx; ++idx) {
472276da39aSCy Schubert msyslog(LOG_WARNING,
473276da39aSCy Schubert "GPSD_JSON: failed to resolve '%s:%s', rc=%d (%s)",
474276da39aSCy Schubert s_svctab[idx][0], s_svctab[idx][1],
475276da39aSCy Schubert s_svcerr[idx], gai_strerror(s_svcerr[idx]));
476276da39aSCy Schubert }
477276da39aSCy Schubert
478276da39aSCy Schubert /* check if it was fatal, or if we can proceed */
479276da39aSCy Schubert if (s_gpsd_addr == NULL)
480276da39aSCy Schubert msyslog(LOG_ERR, "%s",
481276da39aSCy Schubert "GPSD_JSON: failed to get socket address, giving up.");
482276da39aSCy Schubert else if (idx != 0)
483276da39aSCy Schubert msyslog(LOG_WARNING,
484276da39aSCy Schubert "GPSD_JSON: using '%s:%s' instead of '%s:%s'",
485276da39aSCy Schubert s_svctab[idx][0], s_svctab[idx][1],
486276da39aSCy Schubert s_svctab[0][0], s_svctab[0][1]);
487276da39aSCy Schubert
488276da39aSCy Schubert /* make sure this gets logged only once and tell if we can
489276da39aSCy Schubert * proceed or not
490276da39aSCy Schubert */
491276da39aSCy Schubert s_svcidx = 0;
492276da39aSCy Schubert return (s_gpsd_addr != NULL);
493276da39aSCy Schubert }
4942b15cb3dSCy Schubert
4952b15cb3dSCy Schubert /* ---------------------------------------------------------------------
4962b15cb3dSCy Schubert * Start: allocate a unit pointer and set up the runtime data
4972b15cb3dSCy Schubert */
4982b15cb3dSCy Schubert static int
gpsd_start(int unit,peerT * peer)4992b15cb3dSCy Schubert gpsd_start(
5002b15cb3dSCy Schubert int unit,
5012b15cb3dSCy Schubert peerT * peer)
5022b15cb3dSCy Schubert {
5032b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
504276da39aSCy Schubert gpsd_unitT * up;
505276da39aSCy Schubert gpsd_unitT ** uscan = &s_clock_units;
506*a466cc55SCy Schubert const char *tmpName;
5072b15cb3dSCy Schubert
5082b15cb3dSCy Schubert struct stat sb;
509*a466cc55SCy Schubert char * devname = NULL;
5102b15cb3dSCy Schubert
511276da39aSCy Schubert /* check if we can proceed at all or if init failed */
512276da39aSCy Schubert if ( ! gpsd_init_check())
513276da39aSCy Schubert return FALSE;
514276da39aSCy Schubert
515276da39aSCy Schubert /* search for matching unit */
516276da39aSCy Schubert while ((up = *uscan) != NULL && up->unit != (unit & 0x7F))
517276da39aSCy Schubert uscan = &up->next_unit;
518276da39aSCy Schubert if (up == NULL) {
519276da39aSCy Schubert /* alloc unit, add to list and increment use count ASAP. */
520276da39aSCy Schubert up = emalloc_zero(sizeof(*up));
521276da39aSCy Schubert *uscan = up;
522276da39aSCy Schubert ++up->refcount;
523276da39aSCy Schubert
5242b15cb3dSCy Schubert /* initialize the unit structure */
525276da39aSCy Schubert up->logname = estrdup(refnumtoa(&peer->srcadr));
526276da39aSCy Schubert up->unit = unit & 0x7F;
5272b15cb3dSCy Schubert up->fdt = -1;
5282b15cb3dSCy Schubert up->addr = s_gpsd_addr;
5292b15cb3dSCy Schubert up->tickpres = TICKOVER_LOW;
5302b15cb3dSCy Schubert
531276da39aSCy Schubert /* Create the device name and check for a Character
532276da39aSCy Schubert * Device. It's assumed that GPSD was started with the
533276da39aSCy Schubert * same link, so the names match. (If this is not
534276da39aSCy Schubert * practicable, we will have to read the symlink, if
535276da39aSCy Schubert * any, so we can get the true device file.)
536276da39aSCy Schubert */
537*a466cc55SCy Schubert tmpName = clockdev_lookup(&peer->srcadr, 0);
538*a466cc55SCy Schubert if (NULL != tmpName) {
539*a466cc55SCy Schubert up->device = estrdup(tmpName);
540*a466cc55SCy Schubert } else if (-1 == myasprintf(&up->device, "%s%u", s_dev_stem, up->unit)) {
541276da39aSCy Schubert msyslog(LOG_ERR, "%s: clock device name too long",
542276da39aSCy Schubert up->logname);
543276da39aSCy Schubert goto dev_fail;
544276da39aSCy Schubert }
545*a466cc55SCy Schubert devname = up->device;
546*a466cc55SCy Schubert up->device = ntp_realpath(devname);
547*a466cc55SCy Schubert if (NULL == up->device) {
548*a466cc55SCy Schubert msyslog(LOG_ERR, "%s: '%s' has no absolute path",
549*a466cc55SCy Schubert up->logname, devname);
550*a466cc55SCy Schubert goto dev_fail;
551*a466cc55SCy Schubert }
552*a466cc55SCy Schubert free(devname);
553*a466cc55SCy Schubert devname = NULL;
554*a466cc55SCy Schubert if (-1 == lstat(up->device, &sb)) {
555*a466cc55SCy Schubert msyslog(LOG_ERR, "%s: '%s' not accessible",
556*a466cc55SCy Schubert up->logname, up->device);
557*a466cc55SCy Schubert goto dev_fail;
558*a466cc55SCy Schubert }
559*a466cc55SCy Schubert if (!S_ISCHR(sb.st_mode)) {
560276da39aSCy Schubert msyslog(LOG_ERR, "%s: '%s' is not a character device",
561276da39aSCy Schubert up->logname, up->device);
562276da39aSCy Schubert goto dev_fail;
563276da39aSCy Schubert }
564276da39aSCy Schubert } else {
565276da39aSCy Schubert /* All set up, just increment use count. */
566276da39aSCy Schubert ++up->refcount;
567276da39aSCy Schubert }
568276da39aSCy Schubert
5692b15cb3dSCy Schubert /* setup refclock processing */
5702b15cb3dSCy Schubert pp->unitptr = (caddr_t)up;
5712b15cb3dSCy Schubert pp->io.fd = -1;
5722b15cb3dSCy Schubert pp->io.clock_recv = gpsd_receive;
5732b15cb3dSCy Schubert pp->io.srcclock = peer;
5742b15cb3dSCy Schubert pp->io.datalen = 0;
5752b15cb3dSCy Schubert pp->a_lastcode[0] = '\0';
5762b15cb3dSCy Schubert pp->lencode = 0;
5772b15cb3dSCy Schubert pp->clockdesc = DESCRIPTION;
5782b15cb3dSCy Schubert memcpy(&pp->refid, REFID, 4);
5792b15cb3dSCy Schubert
5802b15cb3dSCy Schubert /* Initialize miscellaneous variables */
581276da39aSCy Schubert if (unit >= 128)
582276da39aSCy Schubert peer->precision = PPS_PRECISION;
583276da39aSCy Schubert else
5842b15cb3dSCy Schubert peer->precision = PRECISION;
5852b15cb3dSCy Schubert
586276da39aSCy Schubert /* If the daemon name lookup failed, just give up now. */
587276da39aSCy Schubert if (NULL == up->addr) {
588276da39aSCy Schubert msyslog(LOG_ERR, "%s: no GPSD socket address, giving up",
589276da39aSCy Schubert up->logname);
5902b15cb3dSCy Schubert goto dev_fail;
5912b15cb3dSCy Schubert }
592276da39aSCy Schubert
5932b15cb3dSCy Schubert LOGIF(CLOCKINFO,
5942b15cb3dSCy Schubert (LOG_NOTICE, "%s: startup, device is '%s'",
5952b15cb3dSCy Schubert refnumtoa(&peer->srcadr), up->device));
596276da39aSCy Schubert up->mode = MODE_OP_MODE(peer->ttl);
597276da39aSCy Schubert if (up->mode > MODE_OP_MAXVAL)
598276da39aSCy Schubert up->mode = 0;
599276da39aSCy Schubert if (unit >= 128)
600276da39aSCy Schubert up->pps_peer = peer;
601276da39aSCy Schubert else
602276da39aSCy Schubert enter_opmode(peer, up->mode);
6032b15cb3dSCy Schubert return TRUE;
6042b15cb3dSCy Schubert
6052b15cb3dSCy Schubert dev_fail:
6062b15cb3dSCy Schubert /* On failure, remove all UNIT ressources and declare defeat. */
607*a466cc55SCy Schubert free(devname);
6082b15cb3dSCy Schubert INSIST (up);
609276da39aSCy Schubert if (!--up->refcount) {
610276da39aSCy Schubert *uscan = up->next_unit;
6112b15cb3dSCy Schubert free(up->device);
6122b15cb3dSCy Schubert free(up);
613276da39aSCy Schubert }
6142b15cb3dSCy Schubert
6152b15cb3dSCy Schubert pp->unitptr = (caddr_t)NULL;
6162b15cb3dSCy Schubert return FALSE;
6172b15cb3dSCy Schubert }
6182b15cb3dSCy Schubert
6192b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
6202b15cb3dSCy Schubert
6212b15cb3dSCy Schubert static void
gpsd_shutdown(int unit,peerT * peer)6222b15cb3dSCy Schubert gpsd_shutdown(
6232b15cb3dSCy Schubert int unit,
6242b15cb3dSCy Schubert peerT * peer)
6252b15cb3dSCy Schubert {
6262b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
6272b15cb3dSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
628276da39aSCy Schubert gpsd_unitT ** uscan = &s_clock_units;
6292b15cb3dSCy Schubert
6302b15cb3dSCy Schubert UNUSED_ARG(unit);
6312b15cb3dSCy Schubert
632276da39aSCy Schubert /* The unit pointer might have been removed already. */
633276da39aSCy Schubert if (up == NULL)
634276da39aSCy Schubert return;
635276da39aSCy Schubert
636276da39aSCy Schubert /* now check if we must close IO resources */
637276da39aSCy Schubert if (peer != up->pps_peer) {
638276da39aSCy Schubert if (-1 != pp->io.fd) {
639276da39aSCy Schubert DPRINTF(1, ("%s: closing clock, fd=%d\n",
640276da39aSCy Schubert up->logname, pp->io.fd));
641276da39aSCy Schubert io_closeclock(&pp->io);
642276da39aSCy Schubert pp->io.fd = -1;
643276da39aSCy Schubert }
644276da39aSCy Schubert if (up->fdt != -1)
645276da39aSCy Schubert close(up->fdt);
646276da39aSCy Schubert }
647276da39aSCy Schubert /* decrement use count and eventually remove this unit. */
648276da39aSCy Schubert if (!--up->refcount) {
649276da39aSCy Schubert /* unlink this unit */
650276da39aSCy Schubert while (*uscan != NULL)
651276da39aSCy Schubert if (*uscan == up)
652276da39aSCy Schubert *uscan = up->next_unit;
653276da39aSCy Schubert else
654276da39aSCy Schubert uscan = &(*uscan)->next_unit;
655276da39aSCy Schubert free(up->logname);
6562b15cb3dSCy Schubert free(up->device);
6572b15cb3dSCy Schubert free(up);
6582b15cb3dSCy Schubert }
6592b15cb3dSCy Schubert pp->unitptr = (caddr_t)NULL;
6602b15cb3dSCy Schubert LOGIF(CLOCKINFO,
6612b15cb3dSCy Schubert (LOG_NOTICE, "%s: shutdown", refnumtoa(&peer->srcadr)));
6622b15cb3dSCy Schubert }
6632b15cb3dSCy Schubert
6642b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
6652b15cb3dSCy Schubert
6662b15cb3dSCy Schubert static void
gpsd_receive(struct recvbuf * rbufp)6672b15cb3dSCy Schubert gpsd_receive(
6682b15cb3dSCy Schubert struct recvbuf * rbufp)
6692b15cb3dSCy Schubert {
6702b15cb3dSCy Schubert /* declare & init control structure ptrs */
6712b15cb3dSCy Schubert peerT * const peer = rbufp->recv_peer;
6722b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
6732b15cb3dSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
6742b15cb3dSCy Schubert
6752b15cb3dSCy Schubert const char *psrc, *esrc;
6762b15cb3dSCy Schubert char *pdst, *edst, ch;
6772b15cb3dSCy Schubert
678276da39aSCy Schubert /* log the data stream, if this is enabled */
679*a466cc55SCy Schubert log_data(peer, 3, "recv", (const char*)rbufp->recv_buffer,
680276da39aSCy Schubert (size_t)rbufp->recv_length);
681276da39aSCy Schubert
682276da39aSCy Schubert
6832b15cb3dSCy Schubert /* Since we're getting a raw stream data, we must assemble lines
6842b15cb3dSCy Schubert * in our receive buffer. We can't use neither 'refclock_gtraw'
6852b15cb3dSCy Schubert * not 'refclock_gtlin' here... We process chars until we reach
6862b15cb3dSCy Schubert * an EoL (that is, line feed) but we truncate the message if it
6872b15cb3dSCy Schubert * does not fit the buffer. GPSD might truncate messages, too,
6882b15cb3dSCy Schubert * so dealing with truncated buffers is necessary anyway.
6892b15cb3dSCy Schubert */
6902b15cb3dSCy Schubert psrc = (const char*)rbufp->recv_buffer;
6912b15cb3dSCy Schubert esrc = psrc + rbufp->recv_length;
6922b15cb3dSCy Schubert
6932b15cb3dSCy Schubert pdst = up->buffer + up->buflen;
694*a466cc55SCy Schubert edst = up->buffer + sizeof(up->buffer) - 1; /* for trailing NUL */
6952b15cb3dSCy Schubert
6962b15cb3dSCy Schubert while (psrc != esrc) {
6972b15cb3dSCy Schubert ch = *psrc++;
6982b15cb3dSCy Schubert if (ch == '\n') {
6992b15cb3dSCy Schubert /* trim trailing whitespace & terminate buffer */
7002b15cb3dSCy Schubert while (pdst != up->buffer && pdst[-1] <= ' ')
7012b15cb3dSCy Schubert --pdst;
7022b15cb3dSCy Schubert *pdst = '\0';
7032b15cb3dSCy Schubert /* process data and reset buffer */
704276da39aSCy Schubert up->buflen = pdst - up->buffer;
7052b15cb3dSCy Schubert gpsd_parse(peer, &rbufp->recv_time);
7062b15cb3dSCy Schubert pdst = up->buffer;
7072b15cb3dSCy Schubert } else if (pdst != edst) {
7082b15cb3dSCy Schubert /* add next char, ignoring leading whitespace */
7092b15cb3dSCy Schubert if (ch > ' ' || pdst != up->buffer)
7102b15cb3dSCy Schubert *pdst++ = ch;
7112b15cb3dSCy Schubert }
7122b15cb3dSCy Schubert }
7132b15cb3dSCy Schubert up->buflen = pdst - up->buffer;
7142b15cb3dSCy Schubert up->tickover = TICKOVER_LOW;
7152b15cb3dSCy Schubert }
7162b15cb3dSCy Schubert
7172b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
7182b15cb3dSCy Schubert
7192b15cb3dSCy Schubert static void
poll_primary(peerT * const peer,clockprocT * const pp,gpsd_unitT * const up)720276da39aSCy Schubert poll_primary(
721276da39aSCy Schubert peerT * const peer ,
722276da39aSCy Schubert clockprocT * const pp ,
723276da39aSCy Schubert gpsd_unitT * const up )
724276da39aSCy Schubert {
725276da39aSCy Schubert if (pp->coderecv != pp->codeproc) {
726276da39aSCy Schubert /* all is well */
727276da39aSCy Schubert pp->lastref = pp->lastrec;
728276da39aSCy Schubert refclock_report(peer, CEVNT_NOMINAL);
729276da39aSCy Schubert refclock_receive(peer);
730276da39aSCy Schubert } else {
731276da39aSCy Schubert /* Not working properly, admit to it. If we have no
732276da39aSCy Schubert * connection to GPSD, declare the clock as faulty. If
733276da39aSCy Schubert * there were bad replies, this is handled as the major
734276da39aSCy Schubert * cause, and everything else is just a timeout.
735276da39aSCy Schubert */
736276da39aSCy Schubert peer->precision = PRECISION;
737276da39aSCy Schubert if (-1 == pp->io.fd)
738276da39aSCy Schubert refclock_report(peer, CEVNT_FAULT);
739276da39aSCy Schubert else if (0 != up->tc_breply)
740276da39aSCy Schubert refclock_report(peer, CEVNT_BADREPLY);
741276da39aSCy Schubert else
742276da39aSCy Schubert refclock_report(peer, CEVNT_TIMEOUT);
743276da39aSCy Schubert }
744276da39aSCy Schubert
745276da39aSCy Schubert if (pp->sloppyclockflag & CLK_FLAG4)
746276da39aSCy Schubert mprintf_clock_stats(
747276da39aSCy Schubert &peer->srcadr,"%u %u %u %u %u %u %u",
748276da39aSCy Schubert up->tc_recv,
749276da39aSCy Schubert up->tc_breply, up->tc_nosync,
750276da39aSCy Schubert up->tc_sti_recv, up->tc_sti_used,
751276da39aSCy Schubert up->tc_pps_recv, up->tc_pps_used);
752276da39aSCy Schubert
753276da39aSCy Schubert /* clear tallies for next round */
754276da39aSCy Schubert up->tc_breply = 0;
755276da39aSCy Schubert up->tc_recv = 0;
756276da39aSCy Schubert up->tc_nosync = 0;
757276da39aSCy Schubert up->tc_sti_recv = 0;
758276da39aSCy Schubert up->tc_sti_used = 0;
759276da39aSCy Schubert up->tc_pps_recv = 0;
760276da39aSCy Schubert up->tc_pps_used = 0;
761276da39aSCy Schubert }
762276da39aSCy Schubert
763276da39aSCy Schubert static void
poll_secondary(peerT * const peer,clockprocT * const pp,gpsd_unitT * const up)764276da39aSCy Schubert poll_secondary(
765276da39aSCy Schubert peerT * const peer ,
766276da39aSCy Schubert clockprocT * const pp ,
767276da39aSCy Schubert gpsd_unitT * const up )
768276da39aSCy Schubert {
769276da39aSCy Schubert if (pp->coderecv != pp->codeproc) {
770276da39aSCy Schubert /* all is well */
771276da39aSCy Schubert pp->lastref = pp->lastrec;
772276da39aSCy Schubert refclock_report(peer, CEVNT_NOMINAL);
773276da39aSCy Schubert refclock_receive(peer);
774276da39aSCy Schubert } else {
775276da39aSCy Schubert peer->precision = PPS_PRECISION;
776276da39aSCy Schubert peer->flags &= ~FLAG_PPS;
777276da39aSCy Schubert refclock_report(peer, CEVNT_TIMEOUT);
778276da39aSCy Schubert }
779276da39aSCy Schubert }
780276da39aSCy Schubert
781276da39aSCy Schubert static void
gpsd_poll(int unit,peerT * peer)7822b15cb3dSCy Schubert gpsd_poll(
7832b15cb3dSCy Schubert int unit,
7842b15cb3dSCy Schubert peerT * peer)
7852b15cb3dSCy Schubert {
7862b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
7872b15cb3dSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
7882b15cb3dSCy Schubert
7892b15cb3dSCy Schubert ++pp->polls;
790276da39aSCy Schubert if (peer == up->pps_peer)
791276da39aSCy Schubert poll_secondary(peer, pp, up);
792276da39aSCy Schubert else
793276da39aSCy Schubert poll_primary(peer, pp, up);
7942b15cb3dSCy Schubert }
7952b15cb3dSCy Schubert
7962b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
7972b15cb3dSCy Schubert
7982b15cb3dSCy Schubert static void
gpsd_control(int unit,const struct refclockstat * in_st,struct refclockstat * out_st,peerT * peer)7992b15cb3dSCy Schubert gpsd_control(
8002b15cb3dSCy Schubert int unit,
8012b15cb3dSCy Schubert const struct refclockstat * in_st,
8022b15cb3dSCy Schubert struct refclockstat * out_st,
8032b15cb3dSCy Schubert peerT * peer )
8042b15cb3dSCy Schubert {
8052b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
8062b15cb3dSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
8072b15cb3dSCy Schubert
808276da39aSCy Schubert if (peer == up->pps_peer) {
809276da39aSCy Schubert DTOLFP(pp->fudgetime1, &up->pps_fudge2);
810276da39aSCy Schubert if ( ! (pp->sloppyclockflag & CLK_FLAG1))
811276da39aSCy Schubert peer->flags &= ~FLAG_PPS;
812276da39aSCy Schubert } else {
8132b15cb3dSCy Schubert /* save preprocessed fudge times */
8142b15cb3dSCy Schubert DTOLFP(pp->fudgetime1, &up->pps_fudge);
815276da39aSCy Schubert DTOLFP(pp->fudgetime2, &up->sti_fudge);
816276da39aSCy Schubert
817276da39aSCy Schubert if (MODE_OP_MODE(up->mode ^ peer->ttl)) {
818276da39aSCy Schubert leave_opmode(peer, up->mode);
819276da39aSCy Schubert up->mode = MODE_OP_MODE(peer->ttl);
820276da39aSCy Schubert enter_opmode(peer, up->mode);
821276da39aSCy Schubert }
822276da39aSCy Schubert }
8232b15cb3dSCy Schubert }
8242b15cb3dSCy Schubert
8252b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
8262b15cb3dSCy Schubert
8272b15cb3dSCy Schubert static void
timer_primary(peerT * const peer,clockprocT * const pp,gpsd_unitT * const up)828276da39aSCy Schubert timer_primary(
829276da39aSCy Schubert peerT * const peer ,
830276da39aSCy Schubert clockprocT * const pp ,
831276da39aSCy Schubert gpsd_unitT * const up )
8322b15cb3dSCy Schubert {
8332b15cb3dSCy Schubert int rc;
8342b15cb3dSCy Schubert
8352b15cb3dSCy Schubert /* This is used for timeout handling. Nothing that needs
8362b15cb3dSCy Schubert * sub-second precison happens here, so receive/connect/retry
8372b15cb3dSCy Schubert * timeouts are simply handled by a count down, and then we
8382b15cb3dSCy Schubert * decide what to do by the socket values.
8392b15cb3dSCy Schubert *
8402b15cb3dSCy Schubert * Note that the timer stays at zero here, unless some of the
8412b15cb3dSCy Schubert * functions set it to another value.
8422b15cb3dSCy Schubert */
8432b15cb3dSCy Schubert if (up->logthrottle)
8442b15cb3dSCy Schubert --up->logthrottle;
8452b15cb3dSCy Schubert if (up->tickover)
8462b15cb3dSCy Schubert --up->tickover;
8472b15cb3dSCy Schubert switch (up->tickover) {
8482b15cb3dSCy Schubert case 4:
849276da39aSCy Schubert /* If we are connected to GPSD, try to get a live signal
850276da39aSCy Schubert * by querying the version. Otherwise just check the
851276da39aSCy Schubert * socket to become ready.
8522b15cb3dSCy Schubert */
8532b15cb3dSCy Schubert if (-1 != pp->io.fd) {
854276da39aSCy Schubert size_t rlen = strlen(s_req_version);
855276da39aSCy Schubert DPRINTF(2, ("%s: timer livecheck: '%s'\n",
856276da39aSCy Schubert up->logname, s_req_version));
857*a466cc55SCy Schubert log_data(peer, 2, "send", s_req_version, rlen);
858276da39aSCy Schubert rc = write(pp->io.fd, s_req_version, rlen);
8592b15cb3dSCy Schubert (void)rc;
8602b15cb3dSCy Schubert } else if (-1 != up->fdt) {
8612b15cb3dSCy Schubert gpsd_test_socket(peer);
8622b15cb3dSCy Schubert }
8632b15cb3dSCy Schubert break;
8642b15cb3dSCy Schubert
8652b15cb3dSCy Schubert case 0:
8662b15cb3dSCy Schubert if (-1 != pp->io.fd)
8672b15cb3dSCy Schubert gpsd_stop_socket(peer);
8682b15cb3dSCy Schubert else if (-1 != up->fdt)
8692b15cb3dSCy Schubert gpsd_test_socket(peer);
8702b15cb3dSCy Schubert else if (NULL != s_gpsd_addr)
8712b15cb3dSCy Schubert gpsd_init_socket(peer);
8722b15cb3dSCy Schubert break;
8732b15cb3dSCy Schubert
8742b15cb3dSCy Schubert default:
8752b15cb3dSCy Schubert if (-1 == pp->io.fd && -1 != up->fdt)
8762b15cb3dSCy Schubert gpsd_test_socket(peer);
8772b15cb3dSCy Schubert }
878276da39aSCy Schubert }
8792b15cb3dSCy Schubert
880276da39aSCy Schubert static void
timer_secondary(peerT * const peer,clockprocT * const pp,gpsd_unitT * const up)881276da39aSCy Schubert timer_secondary(
882276da39aSCy Schubert peerT * const peer ,
883276da39aSCy Schubert clockprocT * const pp ,
884276da39aSCy Schubert gpsd_unitT * const up )
885276da39aSCy Schubert {
886276da39aSCy Schubert /* Reduce the count by one. Flush sample buffer and clear PPS
887276da39aSCy Schubert * flag when this happens.
888276da39aSCy Schubert */
889276da39aSCy Schubert up->ppscount2 = max(0, (up->ppscount2 - 1));
890276da39aSCy Schubert if (0 == up->ppscount2) {
891276da39aSCy Schubert if (pp->coderecv != pp->codeproc) {
892276da39aSCy Schubert refclock_report(peer, CEVNT_TIMEOUT);
893276da39aSCy Schubert pp->coderecv = pp->codeproc;
894276da39aSCy Schubert }
8952b15cb3dSCy Schubert peer->flags &= ~FLAG_PPS;
8962b15cb3dSCy Schubert }
897276da39aSCy Schubert }
898276da39aSCy Schubert
899276da39aSCy Schubert static void
gpsd_timer(int unit,peerT * peer)900276da39aSCy Schubert gpsd_timer(
901276da39aSCy Schubert int unit,
902276da39aSCy Schubert peerT * peer)
903276da39aSCy Schubert {
904276da39aSCy Schubert clockprocT * const pp = peer->procptr;
905276da39aSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
906276da39aSCy Schubert
907276da39aSCy Schubert if (peer == up->pps_peer)
908276da39aSCy Schubert timer_secondary(peer, pp, up);
909276da39aSCy Schubert else
910276da39aSCy Schubert timer_primary(peer, pp, up);
911276da39aSCy Schubert }
912276da39aSCy Schubert
913276da39aSCy Schubert /* =====================================================================
914276da39aSCy Schubert * handle opmode switches
915276da39aSCy Schubert */
916276da39aSCy Schubert
917276da39aSCy Schubert static void
enter_opmode(peerT * peer,int mode)918276da39aSCy Schubert enter_opmode(
919276da39aSCy Schubert peerT *peer,
920276da39aSCy Schubert int mode)
921276da39aSCy Schubert {
922276da39aSCy Schubert clockprocT * const pp = peer->procptr;
923276da39aSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
924276da39aSCy Schubert
925276da39aSCy Schubert DPRINTF(1, ("%s: enter operation mode %d\n",
926276da39aSCy Schubert up->logname, MODE_OP_MODE(mode)));
927276da39aSCy Schubert
928276da39aSCy Schubert if (MODE_OP_MODE(mode) == MODE_OP_AUTO) {
929276da39aSCy Schubert up->fl_rawsti = 0;
930276da39aSCy Schubert up->ppscount = PPS_MAXCOUNT / 2;
931276da39aSCy Schubert }
932276da39aSCy Schubert up->fl_pps = 0;
933276da39aSCy Schubert up->fl_sti = 0;
934276da39aSCy Schubert }
935276da39aSCy Schubert
936276da39aSCy Schubert /* ------------------------------------------------------------------ */
937276da39aSCy Schubert
938276da39aSCy Schubert static void
leave_opmode(peerT * peer,int mode)939276da39aSCy Schubert leave_opmode(
940276da39aSCy Schubert peerT *peer,
941276da39aSCy Schubert int mode)
942276da39aSCy Schubert {
943276da39aSCy Schubert clockprocT * const pp = peer->procptr;
944276da39aSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
945276da39aSCy Schubert
946276da39aSCy Schubert DPRINTF(1, ("%s: leaving operation mode %d\n",
947276da39aSCy Schubert up->logname, MODE_OP_MODE(mode)));
948276da39aSCy Schubert
949276da39aSCy Schubert if (MODE_OP_MODE(mode) == MODE_OP_AUTO) {
950276da39aSCy Schubert up->fl_rawsti = 0;
951276da39aSCy Schubert up->ppscount = 0;
952276da39aSCy Schubert }
953276da39aSCy Schubert up->fl_pps = 0;
954276da39aSCy Schubert up->fl_sti = 0;
955276da39aSCy Schubert }
956276da39aSCy Schubert
957276da39aSCy Schubert /* =====================================================================
958276da39aSCy Schubert * operation mode specific evaluation
959276da39aSCy Schubert */
960276da39aSCy Schubert
961276da39aSCy Schubert static void
add_clock_sample(peerT * const peer,clockprocT * const pp,l_fp stamp,l_fp recvt)962276da39aSCy Schubert add_clock_sample(
963276da39aSCy Schubert peerT * const peer ,
964276da39aSCy Schubert clockprocT * const pp ,
965276da39aSCy Schubert l_fp stamp,
966276da39aSCy Schubert l_fp recvt)
967276da39aSCy Schubert {
968276da39aSCy Schubert pp->lastref = stamp;
969276da39aSCy Schubert if (pp->coderecv == pp->codeproc)
970276da39aSCy Schubert refclock_report(peer, CEVNT_NOMINAL);
9712d4e511cSCy Schubert refclock_process_offset(pp, stamp, recvt, 0.0);
972276da39aSCy Schubert }
973276da39aSCy Schubert
974276da39aSCy Schubert /* ------------------------------------------------------------------ */
975276da39aSCy Schubert
976276da39aSCy Schubert static void
eval_strict(peerT * const peer,clockprocT * const pp,gpsd_unitT * const up)977276da39aSCy Schubert eval_strict(
978276da39aSCy Schubert peerT * const peer ,
979276da39aSCy Schubert clockprocT * const pp ,
980276da39aSCy Schubert gpsd_unitT * const up )
981276da39aSCy Schubert {
982276da39aSCy Schubert if (up->fl_sti && up->fl_pps) {
983276da39aSCy Schubert /* use TPV reference time + PPS receive time */
984276da39aSCy Schubert add_clock_sample(peer, pp, up->sti_stamp, up->pps_recvt);
985276da39aSCy Schubert peer->precision = up->pps_prec;
986276da39aSCy Schubert /* both packets consumed now... */
987276da39aSCy Schubert up->fl_pps = 0;
988276da39aSCy Schubert up->fl_sti = 0;
989276da39aSCy Schubert ++up->tc_sti_used;
990276da39aSCy Schubert }
991276da39aSCy Schubert }
992276da39aSCy Schubert
993276da39aSCy Schubert /* ------------------------------------------------------------------ */
994276da39aSCy Schubert /* PPS processing for the secondary channel. GPSD provides us with full
995276da39aSCy Schubert * timing information, so there's no danger of PLL-locking to the wrong
996276da39aSCy Schubert * second. The belts and suspenders needed for the raw ATOM clock are
997276da39aSCy Schubert * unnecessary here.
998276da39aSCy Schubert */
999276da39aSCy Schubert static void
eval_pps_secondary(peerT * const peer,clockprocT * const pp,gpsd_unitT * const up)1000276da39aSCy Schubert eval_pps_secondary(
1001276da39aSCy Schubert peerT * const peer ,
1002276da39aSCy Schubert clockprocT * const pp ,
1003276da39aSCy Schubert gpsd_unitT * const up )
1004276da39aSCy Schubert {
1005276da39aSCy Schubert if (up->fl_pps2) {
1006276da39aSCy Schubert /* feed data */
1007276da39aSCy Schubert add_clock_sample(peer, pp, up->pps_stamp2, up->pps_recvt2);
1008276da39aSCy Schubert peer->precision = up->pps_prec;
1009276da39aSCy Schubert /* PPS peer flag logic */
1010276da39aSCy Schubert up->ppscount2 = min(PPS2_MAXCOUNT, (up->ppscount2 + 2));
1011276da39aSCy Schubert if ((PPS2_MAXCOUNT == up->ppscount2) &&
1012276da39aSCy Schubert (pp->sloppyclockflag & CLK_FLAG1) )
1013276da39aSCy Schubert peer->flags |= FLAG_PPS;
1014276da39aSCy Schubert /* mark time stamp as burned... */
1015276da39aSCy Schubert up->fl_pps2 = 0;
1016276da39aSCy Schubert ++up->tc_pps_used;
1017276da39aSCy Schubert }
1018276da39aSCy Schubert }
1019276da39aSCy Schubert
1020276da39aSCy Schubert /* ------------------------------------------------------------------ */
1021276da39aSCy Schubert
1022276da39aSCy Schubert static void
eval_serial(peerT * const peer,clockprocT * const pp,gpsd_unitT * const up)1023276da39aSCy Schubert eval_serial(
1024276da39aSCy Schubert peerT * const peer ,
1025276da39aSCy Schubert clockprocT * const pp ,
1026276da39aSCy Schubert gpsd_unitT * const up )
1027276da39aSCy Schubert {
1028276da39aSCy Schubert if (up->fl_sti) {
1029276da39aSCy Schubert add_clock_sample(peer, pp, up->sti_stamp, up->sti_recvt);
1030276da39aSCy Schubert peer->precision = up->sti_prec;
1031276da39aSCy Schubert /* mark time stamp as burned... */
1032276da39aSCy Schubert up->fl_sti = 0;
1033276da39aSCy Schubert ++up->tc_sti_used;
1034276da39aSCy Schubert }
1035276da39aSCy Schubert }
1036276da39aSCy Schubert
1037276da39aSCy Schubert /* ------------------------------------------------------------------ */
1038276da39aSCy Schubert static void
eval_auto(peerT * const peer,clockprocT * const pp,gpsd_unitT * const up)1039276da39aSCy Schubert eval_auto(
1040276da39aSCy Schubert peerT * const peer ,
1041276da39aSCy Schubert clockprocT * const pp ,
1042276da39aSCy Schubert gpsd_unitT * const up )
1043276da39aSCy Schubert {
1044276da39aSCy Schubert /* If there's no TPV available, stop working here... */
1045276da39aSCy Schubert if (!up->fl_sti)
1046276da39aSCy Schubert return;
1047276da39aSCy Schubert
1048276da39aSCy Schubert /* check how to handle STI+PPS: Can PPS be used to augment STI
1049276da39aSCy Schubert * (or vice versae), do we drop the sample because there is a
1050276da39aSCy Schubert * temporary missing PPS signal, or do we feed on STI time
1051276da39aSCy Schubert * stamps alone?
1052276da39aSCy Schubert *
1053276da39aSCy Schubert * Do a counter/threshold dance to decide how to proceed.
1054276da39aSCy Schubert */
1055276da39aSCy Schubert if (up->fl_pps) {
1056276da39aSCy Schubert up->ppscount = min(PPS_MAXCOUNT,
1057276da39aSCy Schubert (up->ppscount + PPS_INCCOUNT));
1058276da39aSCy Schubert if ((PPS_MAXCOUNT == up->ppscount) && up->fl_rawsti) {
1059276da39aSCy Schubert up->fl_rawsti = 0;
1060276da39aSCy Schubert msyslog(LOG_INFO,
1061276da39aSCy Schubert "%s: expect valid PPS from now",
1062276da39aSCy Schubert up->logname);
1063276da39aSCy Schubert }
1064276da39aSCy Schubert } else {
1065276da39aSCy Schubert up->ppscount = max(0, (up->ppscount - PPS_DECCOUNT));
1066276da39aSCy Schubert if ((0 == up->ppscount) && !up->fl_rawsti) {
1067276da39aSCy Schubert up->fl_rawsti = -1;
1068276da39aSCy Schubert msyslog(LOG_WARNING,
1069276da39aSCy Schubert "%s: use TPV alone from now",
1070276da39aSCy Schubert up->logname);
1071276da39aSCy Schubert }
1072276da39aSCy Schubert }
1073276da39aSCy Schubert
1074276da39aSCy Schubert /* now eventually feed the sample */
1075276da39aSCy Schubert if (up->fl_rawsti)
1076276da39aSCy Schubert eval_serial(peer, pp, up);
1077276da39aSCy Schubert else
1078276da39aSCy Schubert eval_strict(peer, pp, up);
1079276da39aSCy Schubert }
10802b15cb3dSCy Schubert
10812b15cb3dSCy Schubert /* =====================================================================
10822b15cb3dSCy Schubert * JSON parsing stuff
10832b15cb3dSCy Schubert */
10842b15cb3dSCy Schubert
1085276da39aSCy Schubert /* ------------------------------------------------------------------ */
1086276da39aSCy Schubert /* Parse a decimal integer with a possible sign. Works like 'strtoll()'
1087276da39aSCy Schubert * or 'strtol()', but with a fixed base of 10 and without eating away
1088276da39aSCy Schubert * leading whitespace. For the error codes, the handling of the end
1089276da39aSCy Schubert * pointer and the return values see 'strtol()'.
1090276da39aSCy Schubert */
1091276da39aSCy Schubert static json_int
strtojint(const char * cp,char ** ep)1092276da39aSCy Schubert strtojint(
1093276da39aSCy Schubert const char *cp, char **ep)
1094276da39aSCy Schubert {
1095276da39aSCy Schubert json_uint accu, limit_lo, limit_hi;
1096276da39aSCy Schubert int flags; /* bit 0: overflow; bit 1: sign */
1097276da39aSCy Schubert const char * hold;
10982b15cb3dSCy Schubert
1099276da39aSCy Schubert /* pointer union to circumvent a tricky/sticky const issue */
1100276da39aSCy Schubert union { const char * c; char * v; } vep;
11012b15cb3dSCy Schubert
1102276da39aSCy Schubert /* store initial value of 'cp' -- see 'strtol()' */
1103276da39aSCy Schubert vep.c = cp;
11042b15cb3dSCy Schubert
1105276da39aSCy Schubert /* Eat away an optional sign and set the limits accordingly: The
1106276da39aSCy Schubert * high limit is the maximum absolute value that can be returned,
1107276da39aSCy Schubert * and the low limit is the biggest value that does not cause an
1108276da39aSCy Schubert * overflow when multiplied with 10. Avoid negation overflows.
1109276da39aSCy Schubert */
1110276da39aSCy Schubert if (*cp == '-') {
1111276da39aSCy Schubert cp += 1;
1112276da39aSCy Schubert flags = 2;
1113276da39aSCy Schubert limit_hi = (json_uint)-(JSON_INT_MIN + 1) + 1;
1114276da39aSCy Schubert } else {
1115276da39aSCy Schubert cp += (*cp == '+');
1116276da39aSCy Schubert flags = 0;
1117276da39aSCy Schubert limit_hi = (json_uint)JSON_INT_MAX;
1118276da39aSCy Schubert }
1119276da39aSCy Schubert limit_lo = limit_hi / 10;
1120276da39aSCy Schubert
1121276da39aSCy Schubert /* Now try to convert a sequence of digits. */
1122276da39aSCy Schubert hold = cp;
1123276da39aSCy Schubert accu = 0;
11249034852cSGleb Smirnoff while (isdigit(*(const u_char*)cp)) {
1125276da39aSCy Schubert flags |= (accu > limit_lo);
11269034852cSGleb Smirnoff accu = accu * 10 + (*(const u_char*)cp++ - '0');
1127276da39aSCy Schubert flags |= (accu > limit_hi);
1128276da39aSCy Schubert }
1129276da39aSCy Schubert /* Check for empty conversion (no digits seen). */
1130276da39aSCy Schubert if (hold != cp)
1131276da39aSCy Schubert vep.c = cp;
1132276da39aSCy Schubert else
1133276da39aSCy Schubert errno = EINVAL; /* accu is still zero */
1134276da39aSCy Schubert /* Check for range overflow */
1135276da39aSCy Schubert if (flags & 1) {
1136276da39aSCy Schubert errno = ERANGE;
1137276da39aSCy Schubert accu = limit_hi;
1138276da39aSCy Schubert }
1139276da39aSCy Schubert /* If possible, store back the end-of-conversion pointer */
1140276da39aSCy Schubert if (ep)
1141276da39aSCy Schubert *ep = vep.v;
1142276da39aSCy Schubert /* If negative, return the negated result if the accu is not
1143276da39aSCy Schubert * zero. Avoid negation overflows.
1144276da39aSCy Schubert */
1145276da39aSCy Schubert if ((flags & 2) && accu)
1146276da39aSCy Schubert return -(json_int)(accu - 1) - 1;
1147276da39aSCy Schubert else
1148276da39aSCy Schubert return (json_int)accu;
1149276da39aSCy Schubert }
11502b15cb3dSCy Schubert
11512b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
11522b15cb3dSCy Schubert
11532b15cb3dSCy Schubert static tok_ref
json_token_skip(const json_ctx * ctx,tok_ref tid)11542b15cb3dSCy Schubert json_token_skip(
11552b15cb3dSCy Schubert const json_ctx * ctx,
11562b15cb3dSCy Schubert tok_ref tid)
11572b15cb3dSCy Schubert {
11584e1ef62aSXin LI if (tid >= 0 && tid < ctx->ntok) {
1159276da39aSCy Schubert int len = ctx->tok[tid].size;
1160276da39aSCy Schubert /* For arrays and objects, the size is the number of
1161276da39aSCy Schubert * ITEMS in the compound. Thats the number of objects in
1162276da39aSCy Schubert * the array, and the number of key/value pairs for
1163276da39aSCy Schubert * objects. In theory, the key must be a string, and we
1164276da39aSCy Schubert * could simply skip one token before skipping the
1165276da39aSCy Schubert * value, which can be anything. We're a bit paranoid
1166276da39aSCy Schubert * and lazy at the same time: We simply double the
1167276da39aSCy Schubert * number of tokens to skip and fall through into the
1168276da39aSCy Schubert * array processing when encountering an object.
1169276da39aSCy Schubert */
1170276da39aSCy Schubert switch (ctx->tok[tid].type) {
1171276da39aSCy Schubert case JSMN_OBJECT:
1172276da39aSCy Schubert len *= 2;
1173276da39aSCy Schubert /* FALLTHROUGH */
1174276da39aSCy Schubert case JSMN_ARRAY:
11752b15cb3dSCy Schubert for (++tid; len; --len)
11762b15cb3dSCy Schubert tid = json_token_skip(ctx, tid);
11772b15cb3dSCy Schubert break;
1178276da39aSCy Schubert
1179276da39aSCy Schubert default:
1180276da39aSCy Schubert ++tid;
1181276da39aSCy Schubert break;
1182276da39aSCy Schubert }
118368ba7e87SXin LI /* The next condition should never be true, but paranoia
118468ba7e87SXin LI * prevails...
118568ba7e87SXin LI */
11864e1ef62aSXin LI if (tid < 0 || tid > ctx->ntok)
11872b15cb3dSCy Schubert tid = ctx->ntok;
1188276da39aSCy Schubert }
11892b15cb3dSCy Schubert return tid;
11902b15cb3dSCy Schubert }
11912b15cb3dSCy Schubert
11922b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
11932b15cb3dSCy Schubert
11942b15cb3dSCy Schubert static int
json_object_lookup(const json_ctx * ctx,tok_ref tid,const char * key,int what)11952b15cb3dSCy Schubert json_object_lookup(
11962b15cb3dSCy Schubert const json_ctx * ctx ,
11972b15cb3dSCy Schubert tok_ref tid ,
1198276da39aSCy Schubert const char * key ,
1199276da39aSCy Schubert int what)
12002b15cb3dSCy Schubert {
12012b15cb3dSCy Schubert int len;
12022b15cb3dSCy Schubert
1203276da39aSCy Schubert if (tid < 0 || tid >= ctx->ntok ||
1204276da39aSCy Schubert ctx->tok[tid].type != JSMN_OBJECT)
12052b15cb3dSCy Schubert return INVALID_TOKEN;
1206276da39aSCy Schubert
12072b15cb3dSCy Schubert len = ctx->tok[tid].size;
1208276da39aSCy Schubert for (++tid; len && tid+1 < ctx->ntok; --len) {
1209276da39aSCy Schubert if (ctx->tok[tid].type != JSMN_STRING) { /* Blooper! */
1210276da39aSCy Schubert tid = json_token_skip(ctx, tid); /* skip key */
1211276da39aSCy Schubert tid = json_token_skip(ctx, tid); /* skip val */
1212276da39aSCy Schubert } else if (strcmp(key, ctx->buf + ctx->tok[tid].start)) {
1213276da39aSCy Schubert tid = json_token_skip(ctx, tid+1); /* skip key+val */
121468ba7e87SXin LI } else if (what < 0 || (u_int)what == ctx->tok[tid+1].type) {
12152b15cb3dSCy Schubert return tid + 1;
1216276da39aSCy Schubert } else {
1217276da39aSCy Schubert break;
1218276da39aSCy Schubert }
1219276da39aSCy Schubert /* if skipping ahead returned an error, bail out here. */
1220276da39aSCy Schubert if (tid < 0)
1221276da39aSCy Schubert break;
12222b15cb3dSCy Schubert }
12232b15cb3dSCy Schubert return INVALID_TOKEN;
12242b15cb3dSCy Schubert }
12252b15cb3dSCy Schubert
12262b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
12272b15cb3dSCy Schubert
1228276da39aSCy Schubert static const char*
json_object_lookup_primitive(const json_ctx * ctx,tok_ref tid,const char * key)1229276da39aSCy Schubert json_object_lookup_primitive(
1230276da39aSCy Schubert const json_ctx * ctx,
1231276da39aSCy Schubert tok_ref tid,
1232276da39aSCy Schubert const char * key)
1233276da39aSCy Schubert {
1234276da39aSCy Schubert tid = json_object_lookup(ctx, tid, key, JSMN_PRIMITIVE);
1235276da39aSCy Schubert if (INVALID_TOKEN != tid)
1236276da39aSCy Schubert return ctx->buf + ctx->tok[tid].start;
1237276da39aSCy Schubert else
1238276da39aSCy Schubert return NULL;
1239276da39aSCy Schubert }
1240276da39aSCy Schubert /* ------------------------------------------------------------------ */
1241276da39aSCy Schubert /* look up a boolean value. This essentially returns a tribool:
1242276da39aSCy Schubert * 0->false, 1->true, (-1)->error/undefined
1243276da39aSCy Schubert */
1244276da39aSCy Schubert static int
json_object_lookup_bool(const json_ctx * ctx,tok_ref tid,const char * key)1245276da39aSCy Schubert json_object_lookup_bool(
1246276da39aSCy Schubert const json_ctx * ctx,
1247276da39aSCy Schubert tok_ref tid,
1248276da39aSCy Schubert const char * key)
1249276da39aSCy Schubert {
1250276da39aSCy Schubert const char *cp;
1251276da39aSCy Schubert cp = json_object_lookup_primitive(ctx, tid, key);
1252276da39aSCy Schubert switch ( cp ? *cp : '\0') {
1253276da39aSCy Schubert case 't': return 1;
1254276da39aSCy Schubert case 'f': return 0;
1255276da39aSCy Schubert default : return -1;
1256276da39aSCy Schubert }
1257276da39aSCy Schubert }
1258276da39aSCy Schubert
1259276da39aSCy Schubert /* ------------------------------------------------------------------ */
1260276da39aSCy Schubert
12612b15cb3dSCy Schubert static const char*
json_object_lookup_string(const json_ctx * ctx,tok_ref tid,const char * key)12622b15cb3dSCy Schubert json_object_lookup_string(
12632b15cb3dSCy Schubert const json_ctx * ctx,
12642b15cb3dSCy Schubert tok_ref tid,
12652b15cb3dSCy Schubert const char * key)
12662b15cb3dSCy Schubert {
1267276da39aSCy Schubert tid = json_object_lookup(ctx, tid, key, JSMN_STRING);
1268276da39aSCy Schubert if (INVALID_TOKEN != tid)
1269276da39aSCy Schubert return ctx->buf + ctx->tok[tid].start;
12702b15cb3dSCy Schubert return NULL;
12712b15cb3dSCy Schubert }
12722b15cb3dSCy Schubert
12732b15cb3dSCy Schubert static const char*
json_object_lookup_string_default(const json_ctx * ctx,tok_ref tid,const char * key,const char * def)12742b15cb3dSCy Schubert json_object_lookup_string_default(
12752b15cb3dSCy Schubert const json_ctx * ctx,
12762b15cb3dSCy Schubert tok_ref tid,
12772b15cb3dSCy Schubert const char * key,
12782b15cb3dSCy Schubert const char * def)
12792b15cb3dSCy Schubert {
1280276da39aSCy Schubert tid = json_object_lookup(ctx, tid, key, JSMN_STRING);
1281276da39aSCy Schubert if (INVALID_TOKEN != tid)
1282276da39aSCy Schubert return ctx->buf + ctx->tok[tid].start;
12832b15cb3dSCy Schubert return def;
12842b15cb3dSCy Schubert }
12852b15cb3dSCy Schubert
12862b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
12872b15cb3dSCy Schubert
12882b15cb3dSCy Schubert static json_int
json_object_lookup_int(const json_ctx * ctx,tok_ref tid,const char * key)12892b15cb3dSCy Schubert json_object_lookup_int(
12902b15cb3dSCy Schubert const json_ctx * ctx,
12912b15cb3dSCy Schubert tok_ref tid,
12922b15cb3dSCy Schubert const char * key)
12932b15cb3dSCy Schubert {
12942b15cb3dSCy Schubert json_int ret;
1295276da39aSCy Schubert const char * cp;
12962b15cb3dSCy Schubert char * ep;
12972b15cb3dSCy Schubert
1298276da39aSCy Schubert cp = json_object_lookup_primitive(ctx, tid, key);
1299276da39aSCy Schubert if (NULL != cp) {
1300276da39aSCy Schubert ret = strtojint(cp, &ep);
1301276da39aSCy Schubert if (cp != ep && '\0' == *ep)
13022b15cb3dSCy Schubert return ret;
1303276da39aSCy Schubert } else {
13042b15cb3dSCy Schubert errno = EINVAL;
1305276da39aSCy Schubert }
13062b15cb3dSCy Schubert return 0;
13072b15cb3dSCy Schubert }
13082b15cb3dSCy Schubert
13092b15cb3dSCy Schubert static json_int
json_object_lookup_int_default(const json_ctx * ctx,tok_ref tid,const char * key,json_int def)13102b15cb3dSCy Schubert json_object_lookup_int_default(
13112b15cb3dSCy Schubert const json_ctx * ctx,
13122b15cb3dSCy Schubert tok_ref tid,
13132b15cb3dSCy Schubert const char * key,
13142b15cb3dSCy Schubert json_int def)
13152b15cb3dSCy Schubert {
1316276da39aSCy Schubert json_int ret;
1317276da39aSCy Schubert const char * cp;
1318276da39aSCy Schubert char * ep;
13192b15cb3dSCy Schubert
1320276da39aSCy Schubert cp = json_object_lookup_primitive(ctx, tid, key);
1321276da39aSCy Schubert if (NULL != cp) {
1322276da39aSCy Schubert ret = strtojint(cp, &ep);
1323276da39aSCy Schubert if (cp != ep && '\0' == *ep)
1324276da39aSCy Schubert return ret;
1325276da39aSCy Schubert }
1326276da39aSCy Schubert return def;
13272b15cb3dSCy Schubert }
13282b15cb3dSCy Schubert
13292b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
1330276da39aSCy Schubert #if 0 /* currently unused */
13312b15cb3dSCy Schubert static double
13322b15cb3dSCy Schubert json_object_lookup_float(
13332b15cb3dSCy Schubert const json_ctx * ctx,
13342b15cb3dSCy Schubert tok_ref tid,
13352b15cb3dSCy Schubert const char * key)
13362b15cb3dSCy Schubert {
13372b15cb3dSCy Schubert double ret;
1338276da39aSCy Schubert const char * cp;
13392b15cb3dSCy Schubert char * ep;
13402b15cb3dSCy Schubert
1341276da39aSCy Schubert cp = json_object_lookup_primitive(ctx, tid, key);
1342276da39aSCy Schubert if (NULL != cp) {
1343276da39aSCy Schubert ret = strtod(cp, &ep);
1344276da39aSCy Schubert if (cp != ep && '\0' == *ep)
13452b15cb3dSCy Schubert return ret;
1346276da39aSCy Schubert } else {
13472b15cb3dSCy Schubert errno = EINVAL;
1348276da39aSCy Schubert }
13492b15cb3dSCy Schubert return 0.0;
13502b15cb3dSCy Schubert }
1351276da39aSCy Schubert #endif
13522b15cb3dSCy Schubert
13532b15cb3dSCy Schubert static double
json_object_lookup_float_default(const json_ctx * ctx,tok_ref tid,const char * key,double def)13542b15cb3dSCy Schubert json_object_lookup_float_default(
13552b15cb3dSCy Schubert const json_ctx * ctx,
13562b15cb3dSCy Schubert tok_ref tid,
13572b15cb3dSCy Schubert const char * key,
13582b15cb3dSCy Schubert double def)
13592b15cb3dSCy Schubert {
1360276da39aSCy Schubert double ret;
1361276da39aSCy Schubert const char * cp;
1362276da39aSCy Schubert char * ep;
13632b15cb3dSCy Schubert
1364276da39aSCy Schubert cp = json_object_lookup_primitive(ctx, tid, key);
1365276da39aSCy Schubert if (NULL != cp) {
1366276da39aSCy Schubert ret = strtod(cp, &ep);
1367276da39aSCy Schubert if (cp != ep && '\0' == *ep)
1368276da39aSCy Schubert return ret;
1369276da39aSCy Schubert }
1370276da39aSCy Schubert return def;
13712b15cb3dSCy Schubert }
13722b15cb3dSCy Schubert
13732b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
13742b15cb3dSCy Schubert
13752b15cb3dSCy Schubert static BOOL
json_parse_record(json_ctx * ctx,char * buf,size_t len)13762b15cb3dSCy Schubert json_parse_record(
13772b15cb3dSCy Schubert json_ctx * ctx,
1378276da39aSCy Schubert char * buf,
1379276da39aSCy Schubert size_t len)
13802b15cb3dSCy Schubert {
13812b15cb3dSCy Schubert jsmn_parser jsm;
13822b15cb3dSCy Schubert int idx, rc;
13832b15cb3dSCy Schubert
13842b15cb3dSCy Schubert jsmn_init(&jsm);
1385276da39aSCy Schubert rc = jsmn_parse(&jsm, buf, len, ctx->tok, JSMN_MAXTOK);
1386276da39aSCy Schubert if (rc <= 0)
1387276da39aSCy Schubert return FALSE;
13882b15cb3dSCy Schubert ctx->buf = buf;
1389276da39aSCy Schubert ctx->ntok = rc;
1390276da39aSCy Schubert
1391276da39aSCy Schubert if (JSMN_OBJECT != ctx->tok[0].type)
1392276da39aSCy Schubert return FALSE; /* not object!?! */
13932b15cb3dSCy Schubert
13942b15cb3dSCy Schubert /* Make all tokens NUL terminated by overwriting the
1395276da39aSCy Schubert * terminator symbol. Makes string compares and number parsing a
1396276da39aSCy Schubert * lot easier!
13972b15cb3dSCy Schubert */
1398276da39aSCy Schubert for (idx = 0; idx < ctx->ntok; ++idx)
13992b15cb3dSCy Schubert if (ctx->tok[idx].end > ctx->tok[idx].start)
14002b15cb3dSCy Schubert ctx->buf[ctx->tok[idx].end] = '\0';
14012b15cb3dSCy Schubert return TRUE;
14022b15cb3dSCy Schubert }
14032b15cb3dSCy Schubert
14042b15cb3dSCy Schubert
14052b15cb3dSCy Schubert /* =====================================================================
14062b15cb3dSCy Schubert * static local helpers
14072b15cb3dSCy Schubert */
1408276da39aSCy Schubert static BOOL
get_binary_time(l_fp * const dest,json_ctx * const jctx,const char * const time_name,const char * const frac_name,long fscale)1409276da39aSCy Schubert get_binary_time(
1410276da39aSCy Schubert l_fp * const dest ,
1411276da39aSCy Schubert json_ctx * const jctx ,
1412276da39aSCy Schubert const char * const time_name,
1413276da39aSCy Schubert const char * const frac_name,
1414276da39aSCy Schubert long fscale )
1415276da39aSCy Schubert {
1416276da39aSCy Schubert BOOL retv = FALSE;
1417276da39aSCy Schubert struct timespec ts;
1418276da39aSCy Schubert
1419276da39aSCy Schubert errno = 0;
1420276da39aSCy Schubert ts.tv_sec = (time_t)json_object_lookup_int(jctx, 0, time_name);
1421276da39aSCy Schubert ts.tv_nsec = (long )json_object_lookup_int(jctx, 0, frac_name);
1422276da39aSCy Schubert if (0 == errno) {
1423276da39aSCy Schubert ts.tv_nsec *= fscale;
1424276da39aSCy Schubert *dest = tspec_stamp_to_lfp(ts);
1425276da39aSCy Schubert retv = TRUE;
1426276da39aSCy Schubert }
1427276da39aSCy Schubert return retv;
1428276da39aSCy Schubert }
14292b15cb3dSCy Schubert
14302b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
14312b15cb3dSCy Schubert /* Process a WATCH record
14322b15cb3dSCy Schubert *
14332b15cb3dSCy Schubert * Currently this is only used to recognise that the device is present
14342b15cb3dSCy Schubert * and that we're listed subscribers.
14352b15cb3dSCy Schubert */
14362b15cb3dSCy Schubert static void
process_watch(peerT * const peer,json_ctx * const jctx,const l_fp * const rtime)14372b15cb3dSCy Schubert process_watch(
14382b15cb3dSCy Schubert peerT * const peer ,
14392b15cb3dSCy Schubert json_ctx * const jctx ,
14402b15cb3dSCy Schubert const l_fp * const rtime)
14412b15cb3dSCy Schubert {
14422b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
14432b15cb3dSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
14442b15cb3dSCy Schubert
1445276da39aSCy Schubert const char * path;
1446276da39aSCy Schubert
1447276da39aSCy Schubert path = json_object_lookup_string(jctx, 0, "device");
1448276da39aSCy Schubert if (NULL == path || strcmp(path, up->device))
1449276da39aSCy Schubert return;
1450276da39aSCy Schubert
1451276da39aSCy Schubert if (json_object_lookup_bool(jctx, 0, "enable") > 0 &&
1452276da39aSCy Schubert json_object_lookup_bool(jctx, 0, "json" ) > 0 )
14532b15cb3dSCy Schubert up->fl_watch = -1;
1454276da39aSCy Schubert else
1455276da39aSCy Schubert up->fl_watch = 0;
1456276da39aSCy Schubert DPRINTF(2, ("%s: process_watch, enabled=%d\n",
1457276da39aSCy Schubert up->logname, (up->fl_watch & 1)));
14582b15cb3dSCy Schubert }
14592b15cb3dSCy Schubert
14602b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
14612b15cb3dSCy Schubert
14622b15cb3dSCy Schubert static void
process_version(peerT * const peer,json_ctx * const jctx,const l_fp * const rtime)14632b15cb3dSCy Schubert process_version(
14642b15cb3dSCy Schubert peerT * const peer ,
14652b15cb3dSCy Schubert json_ctx * const jctx ,
14662b15cb3dSCy Schubert const l_fp * const rtime)
14672b15cb3dSCy Schubert {
14682b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
14692b15cb3dSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
14702b15cb3dSCy Schubert
14712b15cb3dSCy Schubert int len;
14722b15cb3dSCy Schubert char * buf;
14732b15cb3dSCy Schubert const char *revision;
14742b15cb3dSCy Schubert const char *release;
1475276da39aSCy Schubert uint16_t pvhi, pvlo;
14762b15cb3dSCy Schubert
14772b15cb3dSCy Schubert /* get protocol version number */
14782b15cb3dSCy Schubert revision = json_object_lookup_string_default(
14792b15cb3dSCy Schubert jctx, 0, "rev", "(unknown)");
14802b15cb3dSCy Schubert release = json_object_lookup_string_default(
14812b15cb3dSCy Schubert jctx, 0, "release", "(unknown)");
14822b15cb3dSCy Schubert errno = 0;
1483276da39aSCy Schubert pvhi = (uint16_t)json_object_lookup_int(jctx, 0, "proto_major");
1484276da39aSCy Schubert pvlo = (uint16_t)json_object_lookup_int(jctx, 0, "proto_minor");
1485276da39aSCy Schubert
14862b15cb3dSCy Schubert if (0 == errno) {
1487276da39aSCy Schubert if ( ! up->fl_vers)
14882b15cb3dSCy Schubert msyslog(LOG_INFO,
14892b15cb3dSCy Schubert "%s: GPSD revision=%s release=%s protocol=%u.%u",
1490276da39aSCy Schubert up->logname, revision, release,
1491276da39aSCy Schubert pvhi, pvlo);
1492276da39aSCy Schubert up->proto_version = PROTO_VERSION(pvhi, pvlo);
1493276da39aSCy Schubert up->fl_vers = -1;
1494276da39aSCy Schubert } else {
1495276da39aSCy Schubert if (syslogok(pp, up))
1496276da39aSCy Schubert msyslog(LOG_INFO,
1497276da39aSCy Schubert "%s: could not evaluate version data",
1498276da39aSCy Schubert up->logname);
1499276da39aSCy Schubert return;
15002b15cb3dSCy Schubert }
1501276da39aSCy Schubert /* With the 3.9 GPSD protocol, '*_musec' vanished from the PPS
1502276da39aSCy Schubert * record and was replace by '*_nsec'.
15032b15cb3dSCy Schubert */
1504276da39aSCy Schubert up->pf_nsec = -(up->proto_version >= PROTO_VERSION(3,9));
15052b15cb3dSCy Schubert
1506276da39aSCy Schubert /* With the 3.10 protocol we can get TOFF records for better
1507276da39aSCy Schubert * timing information.
1508276da39aSCy Schubert */
1509276da39aSCy Schubert up->pf_toff = -(up->proto_version >= PROTO_VERSION(3,10));
15102b15cb3dSCy Schubert
1511276da39aSCy Schubert /* request watch for our GPS device if not yet watched.
1512276da39aSCy Schubert *
1513276da39aSCy Schubert * The version string is also sent as a life signal, if we have
1514276da39aSCy Schubert * seen useable data. So if we're already watching the device,
1515276da39aSCy Schubert * skip the request.
1516276da39aSCy Schubert *
15172b15cb3dSCy Schubert * Reuse the input buffer, which is no longer needed in the
15182b15cb3dSCy Schubert * current cycle. Also assume that we can write the watch
15192b15cb3dSCy Schubert * request in one sweep into the socket; since we do not do
15202b15cb3dSCy Schubert * output otherwise, this should always work. (Unless the
15212b15cb3dSCy Schubert * TCP/IP window size gets lower than the length of the
15222b15cb3dSCy Schubert * request. We handle that when it happens.)
15232b15cb3dSCy Schubert */
1524276da39aSCy Schubert if (up->fl_watch)
1525276da39aSCy Schubert return;
1526276da39aSCy Schubert
152768ba7e87SXin LI /* The logon string is actually the ?WATCH command of GPSD,
152868ba7e87SXin LI * using JSON data and selecting the GPS device name we created
1529*a466cc55SCy Schubert * from our unit number. We have an old and a newer version that
153068ba7e87SXin LI * request PPS (and TOFF) transmission.
153168ba7e87SXin LI */
15322b15cb3dSCy Schubert snprintf(up->buffer, sizeof(up->buffer),
153368ba7e87SXin LI "?WATCH={\"device\":\"%s\",\"enable\":true,\"json\":true%s};\r\n",
153468ba7e87SXin LI up->device, (up->pf_toff ? ",\"pps\":true" : ""));
15352b15cb3dSCy Schubert buf = up->buffer;
15362b15cb3dSCy Schubert len = strlen(buf);
1537*a466cc55SCy Schubert log_data(peer, 2, "send", buf, len);
1538276da39aSCy Schubert if (len != write(pp->io.fd, buf, len) && (syslogok(pp, up))) {
15392b15cb3dSCy Schubert /* Note: if the server fails to read our request, the
15402b15cb3dSCy Schubert * resulting data timeout will take care of the
15412b15cb3dSCy Schubert * connection!
15422b15cb3dSCy Schubert */
1543276da39aSCy Schubert msyslog(LOG_ERR, "%s: failed to write watch request (%m)",
1544276da39aSCy Schubert up->logname);
15452b15cb3dSCy Schubert }
15462b15cb3dSCy Schubert }
15472b15cb3dSCy Schubert
15482b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
15492b15cb3dSCy Schubert
15502b15cb3dSCy Schubert static void
process_tpv(peerT * const peer,json_ctx * const jctx,const l_fp * const rtime)15512b15cb3dSCy Schubert process_tpv(
15522b15cb3dSCy Schubert peerT * const peer ,
15532b15cb3dSCy Schubert json_ctx * const jctx ,
15542b15cb3dSCy Schubert const l_fp * const rtime)
15552b15cb3dSCy Schubert {
15562b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
15572b15cb3dSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
15582b15cb3dSCy Schubert
15592b15cb3dSCy Schubert const char * gps_time;
15602b15cb3dSCy Schubert int gps_mode;
1561276da39aSCy Schubert double ept;
15622b15cb3dSCy Schubert int xlog2;
15632b15cb3dSCy Schubert
15642b15cb3dSCy Schubert gps_mode = (int)json_object_lookup_int_default(
15652b15cb3dSCy Schubert jctx, 0, "mode", 0);
15662b15cb3dSCy Schubert
1567276da39aSCy Schubert gps_time = json_object_lookup_string(
1568276da39aSCy Schubert jctx, 0, "time");
15692b15cb3dSCy Schubert
1570276da39aSCy Schubert /* accept time stamps only in 2d or 3d fix */
1571276da39aSCy Schubert if (gps_mode < 2 || NULL == gps_time) {
15722b15cb3dSCy Schubert /* receiver has no fix; tell about and avoid stale data */
1573276da39aSCy Schubert if ( ! up->pf_toff)
1574276da39aSCy Schubert ++up->tc_sti_recv;
1575276da39aSCy Schubert ++up->tc_nosync;
1576276da39aSCy Schubert up->fl_sti = 0;
15772b15cb3dSCy Schubert up->fl_pps = 0;
1578276da39aSCy Schubert up->fl_nosync = -1;
15792b15cb3dSCy Schubert return;
15802b15cb3dSCy Schubert }
1581276da39aSCy Schubert up->fl_nosync = 0;
15822b15cb3dSCy Schubert
1583276da39aSCy Schubert /* convert clock and set resulting ref time, but only if the
1584276da39aSCy Schubert * TOFF sentence is *not* available
1585276da39aSCy Schubert */
1586276da39aSCy Schubert if ( ! up->pf_toff) {
1587276da39aSCy Schubert ++up->tc_sti_recv;
15882b15cb3dSCy Schubert /* save last time code to clock data */
15892b15cb3dSCy Schubert save_ltc(pp, gps_time);
1590276da39aSCy Schubert /* now parse the time string */
1591276da39aSCy Schubert if (convert_ascii_time(&up->sti_stamp, gps_time)) {
1592276da39aSCy Schubert DPRINTF(2, ("%s: process_tpv, stamp='%s',"
1593276da39aSCy Schubert " recvt='%s' mode=%u\n",
1594276da39aSCy Schubert up->logname,
1595276da39aSCy Schubert gmprettydate(&up->sti_stamp),
1596276da39aSCy Schubert gmprettydate(&up->sti_recvt),
15972b15cb3dSCy Schubert gps_mode));
15982b15cb3dSCy Schubert
1599276da39aSCy Schubert /* have to use local receive time as substitute
1600276da39aSCy Schubert * for the real receive time: TPV does not tell
1601276da39aSCy Schubert * us.
1602276da39aSCy Schubert */
1603276da39aSCy Schubert up->sti_local = *rtime;
1604276da39aSCy Schubert up->sti_recvt = *rtime;
1605276da39aSCy Schubert L_SUB(&up->sti_recvt, &up->sti_fudge);
1606276da39aSCy Schubert up->fl_sti = -1;
16072b15cb3dSCy Schubert } else {
1608276da39aSCy Schubert ++up->tc_breply;
1609276da39aSCy Schubert up->fl_sti = 0;
1610276da39aSCy Schubert }
16112b15cb3dSCy Schubert }
16122b15cb3dSCy Schubert
16132b15cb3dSCy Schubert /* Set the precision from the GPSD data
1614276da39aSCy Schubert * Use the ETP field for an estimation of the precision of the
1615276da39aSCy Schubert * serial data. If ETP is not available, use the default serial
1616276da39aSCy Schubert * data presion instead. (Note: The PPS branch has a different
1617276da39aSCy Schubert * precision estimation, since it gets the proper value directly
1618276da39aSCy Schubert * from GPSD!)
16192b15cb3dSCy Schubert */
1620276da39aSCy Schubert ept = json_object_lookup_float_default(jctx, 0, "ept", 2.0e-3);
1621276da39aSCy Schubert ept = frexp(fabs(ept)*0.70710678, &xlog2); /* ~ sqrt(0.5) */
1622276da39aSCy Schubert if (ept < 0.25)
1623276da39aSCy Schubert xlog2 = INT_MIN;
1624276da39aSCy Schubert if (ept > 2.0)
1625276da39aSCy Schubert xlog2 = INT_MAX;
1626276da39aSCy Schubert up->sti_prec = clamped_precision(xlog2);
16272b15cb3dSCy Schubert }
16282b15cb3dSCy Schubert
16292b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
16302b15cb3dSCy Schubert
16312b15cb3dSCy Schubert static void
process_pps(peerT * const peer,json_ctx * const jctx,const l_fp * const rtime)16322b15cb3dSCy Schubert process_pps(
16332b15cb3dSCy Schubert peerT * const peer ,
16342b15cb3dSCy Schubert json_ctx * const jctx ,
16352b15cb3dSCy Schubert const l_fp * const rtime)
16362b15cb3dSCy Schubert {
16372b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
16382b15cb3dSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
16392b15cb3dSCy Schubert
1640276da39aSCy Schubert int xlog2;
16412b15cb3dSCy Schubert
1642276da39aSCy Schubert ++up->tc_pps_recv;
16432b15cb3dSCy Schubert
1644276da39aSCy Schubert /* Bail out if there's indication that time sync is bad or
1645276da39aSCy Schubert * if we're explicitely requested to ignore PPS data.
1646276da39aSCy Schubert */
1647276da39aSCy Schubert if (up->fl_nosync)
1648276da39aSCy Schubert return;
16492b15cb3dSCy Schubert
16502b15cb3dSCy Schubert up->pps_local = *rtime;
1651276da39aSCy Schubert /* Now grab the time values. 'clock_*' is the event time of the
1652276da39aSCy Schubert * pulse measured on the local system clock; 'real_*' is the GPS
1653276da39aSCy Schubert * reference time GPSD associated with the pulse.
1654276da39aSCy Schubert */
1655276da39aSCy Schubert if (up->pf_nsec) {
1656276da39aSCy Schubert if ( ! get_binary_time(&up->pps_recvt2, jctx,
1657276da39aSCy Schubert "clock_sec", "clock_nsec", 1))
1658276da39aSCy Schubert goto fail;
1659276da39aSCy Schubert if ( ! get_binary_time(&up->pps_stamp2, jctx,
1660276da39aSCy Schubert "real_sec", "real_nsec", 1))
1661276da39aSCy Schubert goto fail;
1662276da39aSCy Schubert } else {
1663276da39aSCy Schubert if ( ! get_binary_time(&up->pps_recvt2, jctx,
1664276da39aSCy Schubert "clock_sec", "clock_musec", 1000))
1665276da39aSCy Schubert goto fail;
1666276da39aSCy Schubert if ( ! get_binary_time(&up->pps_stamp2, jctx,
1667276da39aSCy Schubert "real_sec", "real_musec", 1000))
1668276da39aSCy Schubert goto fail;
1669276da39aSCy Schubert }
16702b15cb3dSCy Schubert
1671276da39aSCy Schubert /* Try to read the precision field from the PPS record. If it's
1672276da39aSCy Schubert * not there, take the precision from the serial data.
1673276da39aSCy Schubert */
1674276da39aSCy Schubert xlog2 = json_object_lookup_int_default(
1675276da39aSCy Schubert jctx, 0, "precision", up->sti_prec);
1676276da39aSCy Schubert up->pps_prec = clamped_precision(xlog2);
1677276da39aSCy Schubert
1678276da39aSCy Schubert /* Get fudged receive times for primary & secondary unit */
1679276da39aSCy Schubert up->pps_recvt = up->pps_recvt2;
1680276da39aSCy Schubert L_SUB(&up->pps_recvt , &up->pps_fudge );
1681276da39aSCy Schubert L_SUB(&up->pps_recvt2, &up->pps_fudge2);
1682276da39aSCy Schubert pp->lastrec = up->pps_recvt;
1683276da39aSCy Schubert
1684276da39aSCy Schubert /* Map to nearest full second as reference time stamp for the
1685276da39aSCy Schubert * primary channel. Sanity checks are done in evaluation step.
1686276da39aSCy Schubert */
16872b15cb3dSCy Schubert up->pps_stamp = up->pps_recvt;
16882b15cb3dSCy Schubert L_ADDUF(&up->pps_stamp, 0x80000000u);
16892b15cb3dSCy Schubert up->pps_stamp.l_uf = 0;
16902b15cb3dSCy Schubert
1691276da39aSCy Schubert if (NULL != up->pps_peer)
1692276da39aSCy Schubert save_ltc(up->pps_peer->procptr,
1693276da39aSCy Schubert gmprettydate(&up->pps_stamp2));
1694276da39aSCy Schubert DPRINTF(2, ("%s: PPS record processed,"
1695276da39aSCy Schubert " stamp='%s', recvt='%s'\n",
1696276da39aSCy Schubert up->logname,
1697276da39aSCy Schubert gmprettydate(&up->pps_stamp2),
1698276da39aSCy Schubert gmprettydate(&up->pps_recvt2)));
16992b15cb3dSCy Schubert
1700276da39aSCy Schubert up->fl_pps = (0 != (pp->sloppyclockflag & CLK_FLAG2)) - 1;
1701276da39aSCy Schubert up->fl_pps2 = -1;
17022b15cb3dSCy Schubert return;
17032b15cb3dSCy Schubert
17042b15cb3dSCy Schubert fail:
1705276da39aSCy Schubert DPRINTF(1, ("%s: PPS record processing FAILED\n",
1706276da39aSCy Schubert up->logname));
1707276da39aSCy Schubert ++up->tc_breply;
1708276da39aSCy Schubert }
1709276da39aSCy Schubert
1710276da39aSCy Schubert /* ------------------------------------------------------------------ */
1711276da39aSCy Schubert
1712276da39aSCy Schubert static void
process_toff(peerT * const peer,json_ctx * const jctx,const l_fp * const rtime)1713276da39aSCy Schubert process_toff(
1714276da39aSCy Schubert peerT * const peer ,
1715276da39aSCy Schubert json_ctx * const jctx ,
1716276da39aSCy Schubert const l_fp * const rtime)
1717276da39aSCy Schubert {
1718276da39aSCy Schubert clockprocT * const pp = peer->procptr;
1719276da39aSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
1720276da39aSCy Schubert
1721276da39aSCy Schubert ++up->tc_sti_recv;
1722276da39aSCy Schubert
1723276da39aSCy Schubert /* remember this! */
1724276da39aSCy Schubert up->pf_toff = -1;
1725276da39aSCy Schubert
1726276da39aSCy Schubert /* bail out if there's indication that time sync is bad */
1727276da39aSCy Schubert if (up->fl_nosync)
1728276da39aSCy Schubert return;
1729276da39aSCy Schubert
1730276da39aSCy Schubert if ( ! get_binary_time(&up->sti_recvt, jctx,
1731276da39aSCy Schubert "clock_sec", "clock_nsec", 1))
1732276da39aSCy Schubert goto fail;
1733276da39aSCy Schubert if ( ! get_binary_time(&up->sti_stamp, jctx,
1734276da39aSCy Schubert "real_sec", "real_nsec", 1))
1735276da39aSCy Schubert goto fail;
1736276da39aSCy Schubert L_SUB(&up->sti_recvt, &up->sti_fudge);
1737276da39aSCy Schubert up->sti_local = *rtime;
1738276da39aSCy Schubert up->fl_sti = -1;
1739276da39aSCy Schubert
1740276da39aSCy Schubert save_ltc(pp, gmprettydate(&up->sti_stamp));
1741276da39aSCy Schubert DPRINTF(2, ("%s: TOFF record processed,"
1742276da39aSCy Schubert " stamp='%s', recvt='%s'\n",
1743276da39aSCy Schubert up->logname,
1744276da39aSCy Schubert gmprettydate(&up->sti_stamp),
1745276da39aSCy Schubert gmprettydate(&up->sti_recvt)));
1746276da39aSCy Schubert return;
1747276da39aSCy Schubert
1748276da39aSCy Schubert fail:
1749276da39aSCy Schubert DPRINTF(1, ("%s: TOFF record processing FAILED\n",
1750276da39aSCy Schubert up->logname));
1751276da39aSCy Schubert ++up->tc_breply;
17522b15cb3dSCy Schubert }
17532b15cb3dSCy Schubert
17542b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
17552b15cb3dSCy Schubert
17562b15cb3dSCy Schubert static void
gpsd_parse(peerT * const peer,const l_fp * const rtime)17572b15cb3dSCy Schubert gpsd_parse(
17582b15cb3dSCy Schubert peerT * const peer ,
17592b15cb3dSCy Schubert const l_fp * const rtime)
17602b15cb3dSCy Schubert {
17612b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
17622b15cb3dSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
17632b15cb3dSCy Schubert
17642b15cb3dSCy Schubert const char * clsid;
17652b15cb3dSCy Schubert
1766276da39aSCy Schubert DPRINTF(2, ("%s: gpsd_parse: time %s '%.*s'\n",
1767276da39aSCy Schubert up->logname, ulfptoa(rtime, 6),
1768276da39aSCy Schubert up->buflen, up->buffer));
17692b15cb3dSCy Schubert
1770276da39aSCy Schubert /* See if we can grab anything potentially useful. JSMN does not
1771276da39aSCy Schubert * need a trailing NUL, but it needs the number of bytes to
1772276da39aSCy Schubert * process. */
1773276da39aSCy Schubert if (!json_parse_record(&up->json_parse, up->buffer, up->buflen)) {
1774276da39aSCy Schubert ++up->tc_breply;
17752b15cb3dSCy Schubert return;
1776276da39aSCy Schubert }
17772b15cb3dSCy Schubert
17782b15cb3dSCy Schubert /* Now dispatch over the objects we know */
1779276da39aSCy Schubert clsid = json_object_lookup_string(&up->json_parse, 0, "class");
1780276da39aSCy Schubert if (NULL == clsid) {
1781276da39aSCy Schubert ++up->tc_breply;
1782276da39aSCy Schubert return;
1783276da39aSCy Schubert }
17842b15cb3dSCy Schubert
1785276da39aSCy Schubert if (!strcmp("TPV", clsid))
1786276da39aSCy Schubert process_tpv(peer, &up->json_parse, rtime);
17872b15cb3dSCy Schubert else if (!strcmp("PPS", clsid))
1788276da39aSCy Schubert process_pps(peer, &up->json_parse, rtime);
1789276da39aSCy Schubert else if (!strcmp("TOFF", clsid))
1790276da39aSCy Schubert process_toff(peer, &up->json_parse, rtime);
1791276da39aSCy Schubert else if (!strcmp("VERSION", clsid))
1792276da39aSCy Schubert process_version(peer, &up->json_parse, rtime);
17932b15cb3dSCy Schubert else if (!strcmp("WATCH", clsid))
1794276da39aSCy Schubert process_watch(peer, &up->json_parse, rtime);
17952b15cb3dSCy Schubert else
17962b15cb3dSCy Schubert return; /* nothing we know about... */
1797276da39aSCy Schubert ++up->tc_recv;
17982b15cb3dSCy Schubert
1799276da39aSCy Schubert /* if possible, feed the PPS side channel */
1800276da39aSCy Schubert if (up->pps_peer)
1801276da39aSCy Schubert eval_pps_secondary(
1802276da39aSCy Schubert up->pps_peer, up->pps_peer->procptr, up);
18032b15cb3dSCy Schubert
1804276da39aSCy Schubert /* check PPS vs. STI receive times:
1805276da39aSCy Schubert * If STI is before PPS, then clearly the STI is too old. If PPS
1806276da39aSCy Schubert * is before STI by more than one second, then PPS is too old.
1807276da39aSCy Schubert * Weed out stale time stamps & flags.
1808276da39aSCy Schubert */
1809276da39aSCy Schubert if (up->fl_pps && up->fl_sti) {
1810276da39aSCy Schubert l_fp diff;
1811276da39aSCy Schubert diff = up->sti_local;
1812276da39aSCy Schubert L_SUB(&diff, &up->pps_local);
1813276da39aSCy Schubert if (diff.l_i > 0)
1814276da39aSCy Schubert up->fl_pps = 0; /* pps too old */
1815276da39aSCy Schubert else if (diff.l_i < 0)
1816276da39aSCy Schubert up->fl_sti = 0; /* serial data too old */
18172b15cb3dSCy Schubert }
1818276da39aSCy Schubert
1819276da39aSCy Schubert /* dispatch to the mode-dependent processing functions */
1820276da39aSCy Schubert switch (up->mode) {
1821276da39aSCy Schubert default:
1822276da39aSCy Schubert case MODE_OP_STI:
1823276da39aSCy Schubert eval_serial(peer, pp, up);
1824276da39aSCy Schubert break;
1825276da39aSCy Schubert
1826276da39aSCy Schubert case MODE_OP_STRICT:
1827276da39aSCy Schubert eval_strict(peer, pp, up);
1828276da39aSCy Schubert break;
1829276da39aSCy Schubert
1830276da39aSCy Schubert case MODE_OP_AUTO:
1831276da39aSCy Schubert eval_auto(peer, pp, up);
1832276da39aSCy Schubert break;
18332b15cb3dSCy Schubert }
18342b15cb3dSCy Schubert }
18352b15cb3dSCy Schubert
18362b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
18372b15cb3dSCy Schubert
18382b15cb3dSCy Schubert static void
gpsd_stop_socket(peerT * const peer)18392b15cb3dSCy Schubert gpsd_stop_socket(
18402b15cb3dSCy Schubert peerT * const peer)
18412b15cb3dSCy Schubert {
18422b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
18432b15cb3dSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
18442b15cb3dSCy Schubert
1845276da39aSCy Schubert if (-1 != pp->io.fd) {
18462b15cb3dSCy Schubert if (syslogok(pp, up))
18472b15cb3dSCy Schubert msyslog(LOG_INFO,
1848276da39aSCy Schubert "%s: closing socket to GPSD, fd=%d",
1849276da39aSCy Schubert up->logname, pp->io.fd);
1850276da39aSCy Schubert else
1851276da39aSCy Schubert DPRINTF(1, ("%s: closing socket to GPSD, fd=%d\n",
1852276da39aSCy Schubert up->logname, pp->io.fd));
1853276da39aSCy Schubert io_closeclock(&pp->io);
1854276da39aSCy Schubert pp->io.fd = -1;
1855276da39aSCy Schubert }
18562b15cb3dSCy Schubert up->tickover = up->tickpres;
18572b15cb3dSCy Schubert up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH);
18582b15cb3dSCy Schubert up->fl_vers = 0;
1859276da39aSCy Schubert up->fl_sti = 0;
18602b15cb3dSCy Schubert up->fl_pps = 0;
18612b15cb3dSCy Schubert up->fl_watch = 0;
18622b15cb3dSCy Schubert }
18632b15cb3dSCy Schubert
18642b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
18652b15cb3dSCy Schubert
18662b15cb3dSCy Schubert static void
gpsd_init_socket(peerT * const peer)18672b15cb3dSCy Schubert gpsd_init_socket(
18682b15cb3dSCy Schubert peerT * const peer)
18692b15cb3dSCy Schubert {
18702b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
18712b15cb3dSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
18722b15cb3dSCy Schubert addrinfoT * ai;
18732b15cb3dSCy Schubert int rc;
18742b15cb3dSCy Schubert int ov;
18752b15cb3dSCy Schubert
18762b15cb3dSCy Schubert /* draw next address to try */
18772b15cb3dSCy Schubert if (NULL == up->addr)
18782b15cb3dSCy Schubert up->addr = s_gpsd_addr;
18792b15cb3dSCy Schubert ai = up->addr;
18802b15cb3dSCy Schubert up->addr = ai->ai_next;
18812b15cb3dSCy Schubert
18822b15cb3dSCy Schubert /* try to create a matching socket */
18832b15cb3dSCy Schubert up->fdt = socket(
18842b15cb3dSCy Schubert ai->ai_family, ai->ai_socktype, ai->ai_protocol);
18852b15cb3dSCy Schubert if (-1 == up->fdt) {
18862b15cb3dSCy Schubert if (syslogok(pp, up))
18872b15cb3dSCy Schubert msyslog(LOG_ERR,
18882b15cb3dSCy Schubert "%s: cannot create GPSD socket: %m",
1889276da39aSCy Schubert up->logname);
18902b15cb3dSCy Schubert goto no_socket;
18912b15cb3dSCy Schubert }
18922b15cb3dSCy Schubert
1893276da39aSCy Schubert /* Make sure the socket is non-blocking. Connect/reconnect and
1894276da39aSCy Schubert * IO happen in an event-driven environment, and synchronous
1895276da39aSCy Schubert * operations wreak havoc on that.
1896276da39aSCy Schubert */
18972b15cb3dSCy Schubert rc = fcntl(up->fdt, F_SETFL, O_NONBLOCK, 1);
18982b15cb3dSCy Schubert if (-1 == rc) {
18992b15cb3dSCy Schubert if (syslogok(pp, up))
19002b15cb3dSCy Schubert msyslog(LOG_ERR,
19012b15cb3dSCy Schubert "%s: cannot set GPSD socket to non-blocking: %m",
1902276da39aSCy Schubert up->logname);
19032b15cb3dSCy Schubert goto no_socket;
19042b15cb3dSCy Schubert }
1905276da39aSCy Schubert /* Disable nagling. The way both GPSD and NTPD handle the
1906276da39aSCy Schubert * protocol makes it record-oriented, and in most cases
1907276da39aSCy Schubert * complete records (JSON serialised objects) will be sent in
1908276da39aSCy Schubert * one sweep. Nagling gives not much advantage but adds another
1909276da39aSCy Schubert * delay, which can worsen the situation for some packets.
1910276da39aSCy Schubert */
19112b15cb3dSCy Schubert ov = 1;
19122b15cb3dSCy Schubert rc = setsockopt(up->fdt, IPPROTO_TCP, TCP_NODELAY,
191309100258SXin LI (void *)&ov, sizeof(ov));
19142b15cb3dSCy Schubert if (-1 == rc) {
19152b15cb3dSCy Schubert if (syslogok(pp, up))
19162b15cb3dSCy Schubert msyslog(LOG_INFO,
19172b15cb3dSCy Schubert "%s: cannot disable TCP nagle: %m",
1918276da39aSCy Schubert up->logname);
19192b15cb3dSCy Schubert }
19202b15cb3dSCy Schubert
1921276da39aSCy Schubert /* Start a non-blocking connect. There might be a synchronous
1922276da39aSCy Schubert * connection result we have to handle.
1923276da39aSCy Schubert */
19242b15cb3dSCy Schubert rc = connect(up->fdt, ai->ai_addr, ai->ai_addrlen);
1925276da39aSCy Schubert if (-1 == rc) {
1926276da39aSCy Schubert if (errno == EINPROGRESS) {
1927276da39aSCy Schubert DPRINTF(1, ("%s: async connect pending, fd=%d\n",
1928276da39aSCy Schubert up->logname, up->fdt));
1929276da39aSCy Schubert return;
1930276da39aSCy Schubert }
1931276da39aSCy Schubert
19322b15cb3dSCy Schubert if (syslogok(pp, up))
19332b15cb3dSCy Schubert msyslog(LOG_ERR,
19342b15cb3dSCy Schubert "%s: cannot connect GPSD socket: %m",
1935276da39aSCy Schubert up->logname);
1936276da39aSCy Schubert goto no_socket;
1937276da39aSCy Schubert }
1938276da39aSCy Schubert
1939276da39aSCy Schubert /* We had a successful synchronous connect, so we add the
1940276da39aSCy Schubert * refclock processing ASAP. We still have to wait for the
1941276da39aSCy Schubert * version string and apply the watch command later on, but we
1942276da39aSCy Schubert * might as well get the show on the road now.
1943276da39aSCy Schubert */
1944276da39aSCy Schubert DPRINTF(1, ("%s: new socket connection, fd=%d\n",
1945276da39aSCy Schubert up->logname, up->fdt));
1946276da39aSCy Schubert
1947276da39aSCy Schubert pp->io.fd = up->fdt;
1948276da39aSCy Schubert up->fdt = -1;
1949276da39aSCy Schubert if (0 == io_addclock(&pp->io)) {
1950276da39aSCy Schubert if (syslogok(pp, up))
1951276da39aSCy Schubert msyslog(LOG_ERR,
1952276da39aSCy Schubert "%s: failed to register with I/O engine",
1953276da39aSCy Schubert up->logname);
19542b15cb3dSCy Schubert goto no_socket;
19552b15cb3dSCy Schubert }
19562b15cb3dSCy Schubert
19572b15cb3dSCy Schubert return;
19582b15cb3dSCy Schubert
19592b15cb3dSCy Schubert no_socket:
1960276da39aSCy Schubert if (-1 != pp->io.fd)
1961276da39aSCy Schubert close(pp->io.fd);
19622b15cb3dSCy Schubert if (-1 != up->fdt)
19632b15cb3dSCy Schubert close(up->fdt);
1964276da39aSCy Schubert pp->io.fd = -1;
19652b15cb3dSCy Schubert up->fdt = -1;
19662b15cb3dSCy Schubert up->tickover = up->tickpres;
19672b15cb3dSCy Schubert up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH);
19682b15cb3dSCy Schubert }
19692b15cb3dSCy Schubert
19702b15cb3dSCy Schubert /* ------------------------------------------------------------------ */
19712b15cb3dSCy Schubert
19722b15cb3dSCy Schubert static void
gpsd_test_socket(peerT * const peer)19732b15cb3dSCy Schubert gpsd_test_socket(
19742b15cb3dSCy Schubert peerT * const peer)
19752b15cb3dSCy Schubert {
19762b15cb3dSCy Schubert clockprocT * const pp = peer->procptr;
19772b15cb3dSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
19782b15cb3dSCy Schubert
19792b15cb3dSCy Schubert int ec, rc;
19802b15cb3dSCy Schubert socklen_t lc;
19812b15cb3dSCy Schubert
19822b15cb3dSCy Schubert /* Check if the non-blocking connect was finished by testing the
19832b15cb3dSCy Schubert * socket for writeability. Use the 'poll()' API if available
19842b15cb3dSCy Schubert * and 'select()' otherwise.
19852b15cb3dSCy Schubert */
1986276da39aSCy Schubert DPRINTF(2, ("%s: check connect, fd=%d\n",
1987276da39aSCy Schubert up->logname, up->fdt));
19882b15cb3dSCy Schubert
19892b15cb3dSCy Schubert #if defined(HAVE_SYS_POLL_H)
19902b15cb3dSCy Schubert {
19912b15cb3dSCy Schubert struct pollfd pfd;
19922b15cb3dSCy Schubert
19932b15cb3dSCy Schubert pfd.events = POLLOUT;
19942b15cb3dSCy Schubert pfd.fd = up->fdt;
19952b15cb3dSCy Schubert rc = poll(&pfd, 1, 0);
19962b15cb3dSCy Schubert if (1 != rc || !(pfd.revents & POLLOUT))
19972b15cb3dSCy Schubert return;
19982b15cb3dSCy Schubert }
19992b15cb3dSCy Schubert #elif defined(HAVE_SYS_SELECT_H)
20002b15cb3dSCy Schubert {
20012b15cb3dSCy Schubert struct timeval tout;
20022b15cb3dSCy Schubert fd_set wset;
20032b15cb3dSCy Schubert
20042b15cb3dSCy Schubert memset(&tout, 0, sizeof(tout));
20052b15cb3dSCy Schubert FD_ZERO(&wset);
20062b15cb3dSCy Schubert FD_SET(up->fdt, &wset);
20072b15cb3dSCy Schubert rc = select(up->fdt+1, NULL, &wset, NULL, &tout);
20082b15cb3dSCy Schubert if (0 == rc || !(FD_ISSET(up->fdt, &wset)))
20092b15cb3dSCy Schubert return;
20102b15cb3dSCy Schubert }
20112b15cb3dSCy Schubert #else
20122b15cb3dSCy Schubert # error Blooper! That should have been found earlier!
20132b15cb3dSCy Schubert #endif
20142b15cb3dSCy Schubert
20152b15cb3dSCy Schubert /* next timeout is a full one... */
20162b15cb3dSCy Schubert up->tickover = TICKOVER_LOW;
20172b15cb3dSCy Schubert
20182b15cb3dSCy Schubert /* check for socket error */
20192b15cb3dSCy Schubert ec = 0;
20202b15cb3dSCy Schubert lc = sizeof(ec);
202109100258SXin LI rc = getsockopt(up->fdt, SOL_SOCKET, SO_ERROR, (void *)&ec, &lc);
20222b15cb3dSCy Schubert if (-1 == rc || 0 != ec) {
2023276da39aSCy Schubert const char *errtxt;
2024276da39aSCy Schubert if (0 == ec)
2025276da39aSCy Schubert ec = errno;
2026276da39aSCy Schubert errtxt = strerror(ec);
20272b15cb3dSCy Schubert if (syslogok(pp, up))
20282b15cb3dSCy Schubert msyslog(LOG_ERR,
2029276da39aSCy Schubert "%s: async connect to GPSD failed,"
2030276da39aSCy Schubert " fd=%d, ec=%d(%s)",
2031276da39aSCy Schubert up->logname, up->fdt, ec, errtxt);
2032276da39aSCy Schubert else
2033276da39aSCy Schubert DPRINTF(1, ("%s: async connect to GPSD failed,"
2034276da39aSCy Schubert " fd=%d, ec=%d(%s)\n",
2035276da39aSCy Schubert up->logname, up->fdt, ec, errtxt));
20362b15cb3dSCy Schubert goto no_socket;
2037276da39aSCy Schubert } else {
2038276da39aSCy Schubert DPRINTF(1, ("%s: async connect to GPSD succeeded, fd=%d\n",
2039276da39aSCy Schubert up->logname, up->fdt));
20402b15cb3dSCy Schubert }
2041276da39aSCy Schubert
20422b15cb3dSCy Schubert /* swap socket FDs, and make sure the clock was added */
20432b15cb3dSCy Schubert pp->io.fd = up->fdt;
20442b15cb3dSCy Schubert up->fdt = -1;
20452b15cb3dSCy Schubert if (0 == io_addclock(&pp->io)) {
20462b15cb3dSCy Schubert if (syslogok(pp, up))
20472b15cb3dSCy Schubert msyslog(LOG_ERR,
20482b15cb3dSCy Schubert "%s: failed to register with I/O engine",
2049276da39aSCy Schubert up->logname);
20502b15cb3dSCy Schubert goto no_socket;
20512b15cb3dSCy Schubert }
20522b15cb3dSCy Schubert return;
20532b15cb3dSCy Schubert
20542b15cb3dSCy Schubert no_socket:
2055276da39aSCy Schubert if (-1 != up->fdt) {
2056276da39aSCy Schubert DPRINTF(1, ("%s: closing socket, fd=%d\n",
2057276da39aSCy Schubert up->logname, up->fdt));
20582b15cb3dSCy Schubert close(up->fdt);
2059276da39aSCy Schubert }
20602b15cb3dSCy Schubert up->fdt = -1;
20612b15cb3dSCy Schubert up->tickover = up->tickpres;
20622b15cb3dSCy Schubert up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH);
20632b15cb3dSCy Schubert }
20642b15cb3dSCy Schubert
20652b15cb3dSCy Schubert /* =====================================================================
20662b15cb3dSCy Schubert * helper stuff
20672b15cb3dSCy Schubert */
20682b15cb3dSCy Schubert
2069276da39aSCy Schubert /* -------------------------------------------------------------------
2070276da39aSCy Schubert * store a properly clamped precision value
20712b15cb3dSCy Schubert */
2072276da39aSCy Schubert static int16_t
clamped_precision(int rawprec)2073276da39aSCy Schubert clamped_precision(
2074276da39aSCy Schubert int rawprec)
20752b15cb3dSCy Schubert {
2076276da39aSCy Schubert if (rawprec > 0)
2077276da39aSCy Schubert rawprec = 0;
2078276da39aSCy Schubert if (rawprec < -32)
2079276da39aSCy Schubert rawprec = -32;
2080276da39aSCy Schubert return (int16_t)rawprec;
20812b15cb3dSCy Schubert }
20822b15cb3dSCy Schubert
20832b15cb3dSCy Schubert /* -------------------------------------------------------------------
2084276da39aSCy Schubert * Convert a GPSD timestamp (ISO8601 Format) to an l_fp
20852b15cb3dSCy Schubert */
20862b15cb3dSCy Schubert static BOOL
convert_ascii_time(l_fp * fp,const char * gps_time)20872b15cb3dSCy Schubert convert_ascii_time(
20882b15cb3dSCy Schubert l_fp * fp ,
20892b15cb3dSCy Schubert const char * gps_time)
20902b15cb3dSCy Schubert {
20912b15cb3dSCy Schubert char *ep;
20922b15cb3dSCy Schubert struct tm gd;
20932b15cb3dSCy Schubert struct timespec ts;
2094276da39aSCy Schubert uint32_t dw;
20952b15cb3dSCy Schubert
20962b15cb3dSCy Schubert /* Use 'strptime' to take the brunt of the work, then parse
20972b15cb3dSCy Schubert * the fractional part manually, starting with a digit weight of
20982b15cb3dSCy Schubert * 10^8 nanoseconds.
20992b15cb3dSCy Schubert */
21002b15cb3dSCy Schubert ts.tv_nsec = 0;
21012b15cb3dSCy Schubert ep = strptime(gps_time, "%Y-%m-%dT%H:%M:%S", &gd);
2102276da39aSCy Schubert if (NULL == ep)
2103276da39aSCy Schubert return FALSE; /* could not parse the mandatory stuff! */
21042b15cb3dSCy Schubert if (*ep == '.') {
2105276da39aSCy Schubert dw = 100000000u;
21069034852cSGleb Smirnoff while (isdigit(*(u_char*)++ep)) {
21079034852cSGleb Smirnoff ts.tv_nsec += (*(u_char*)ep - '0') * dw;
2108276da39aSCy Schubert dw /= 10u;
21092b15cb3dSCy Schubert }
21102b15cb3dSCy Schubert }
21112b15cb3dSCy Schubert if (ep[0] != 'Z' || ep[1] != '\0')
2112276da39aSCy Schubert return FALSE; /* trailing garbage */
21132b15cb3dSCy Schubert
2114276da39aSCy Schubert /* Now convert the whole thing into a 'l_fp'. We do not use
2115276da39aSCy Schubert * 'mkgmtime()' since its not standard and going through the
2116276da39aSCy Schubert * calendar routines is not much effort, either.
2117276da39aSCy Schubert */
21182b15cb3dSCy Schubert ts.tv_sec = (ntpcal_tm_to_rd(&gd) - DAY_NTP_STARTS) * SECSPERDAY
21192b15cb3dSCy Schubert + ntpcal_tm_to_daysec(&gd);
21202b15cb3dSCy Schubert *fp = tspec_intv_to_lfp(ts);
21212b15cb3dSCy Schubert
21222b15cb3dSCy Schubert return TRUE;
21232b15cb3dSCy Schubert }
21242b15cb3dSCy Schubert
21252b15cb3dSCy Schubert /* -------------------------------------------------------------------
21262b15cb3dSCy Schubert * Save the last timecode string, making sure it's properly truncated
21272b15cb3dSCy Schubert * if necessary and NUL terminated in any case.
21282b15cb3dSCy Schubert */
21292b15cb3dSCy Schubert static void
save_ltc(clockprocT * const pp,const char * const tc)21302b15cb3dSCy Schubert save_ltc(
21312b15cb3dSCy Schubert clockprocT * const pp,
21322b15cb3dSCy Schubert const char * const tc)
21332b15cb3dSCy Schubert {
2134f0574f5cSXin LI size_t len = 0;
21352b15cb3dSCy Schubert
2136f0574f5cSXin LI if (tc) {
2137f0574f5cSXin LI len = strlen(tc);
21382b15cb3dSCy Schubert if (len >= sizeof(pp->a_lastcode))
21392b15cb3dSCy Schubert len = sizeof(pp->a_lastcode) - 1;
21402b15cb3dSCy Schubert memcpy(pp->a_lastcode, tc, len);
2141f0574f5cSXin LI }
2142f0574f5cSXin LI pp->lencode = (u_short)len;
21432b15cb3dSCy Schubert pp->a_lastcode[len] = '\0';
21442b15cb3dSCy Schubert }
21452b15cb3dSCy Schubert
2146276da39aSCy Schubert /* -------------------------------------------------------------------
21472b15cb3dSCy Schubert * asprintf replacement... it's not available everywhere...
21482b15cb3dSCy Schubert */
21492b15cb3dSCy Schubert static int
myasprintf(char ** spp,char const * fmt,...)21502b15cb3dSCy Schubert myasprintf(
21512b15cb3dSCy Schubert char ** spp,
21522b15cb3dSCy Schubert char const * fmt,
21532b15cb3dSCy Schubert ... )
21542b15cb3dSCy Schubert {
21552b15cb3dSCy Schubert size_t alen, plen;
21562b15cb3dSCy Schubert
21572b15cb3dSCy Schubert alen = 32;
21582b15cb3dSCy Schubert *spp = NULL;
21592b15cb3dSCy Schubert do {
21602b15cb3dSCy Schubert va_list va;
21612b15cb3dSCy Schubert
21622b15cb3dSCy Schubert alen += alen;
21632b15cb3dSCy Schubert free(*spp);
21642b15cb3dSCy Schubert *spp = (char*)malloc(alen);
21652b15cb3dSCy Schubert if (NULL == *spp)
21662b15cb3dSCy Schubert return -1;
21672b15cb3dSCy Schubert
21682b15cb3dSCy Schubert va_start(va, fmt);
21692b15cb3dSCy Schubert plen = (size_t)vsnprintf(*spp, alen, fmt, va);
21702b15cb3dSCy Schubert va_end(va);
21712b15cb3dSCy Schubert } while (plen >= alen);
21722b15cb3dSCy Schubert
21732b15cb3dSCy Schubert return (int)plen;
21742b15cb3dSCy Schubert }
21752b15cb3dSCy Schubert
2176276da39aSCy Schubert /* -------------------------------------------------------------------
2177276da39aSCy Schubert * dump a raw data buffer
2178276da39aSCy Schubert */
2179276da39aSCy Schubert
2180276da39aSCy Schubert static char *
add_string(char * dp,char * ep,const char * sp)2181276da39aSCy Schubert add_string(
2182276da39aSCy Schubert char *dp,
2183276da39aSCy Schubert char *ep,
2184276da39aSCy Schubert const char *sp)
2185276da39aSCy Schubert {
2186276da39aSCy Schubert while (dp != ep && *sp)
2187276da39aSCy Schubert *dp++ = *sp++;
2188276da39aSCy Schubert return dp;
2189276da39aSCy Schubert }
2190276da39aSCy Schubert
2191276da39aSCy Schubert static void
log_data(peerT * peer,int level,const char * what,const char * buf,size_t len)2192276da39aSCy Schubert log_data(
2193276da39aSCy Schubert peerT *peer,
2194*a466cc55SCy Schubert int level,
2195276da39aSCy Schubert const char *what,
2196276da39aSCy Schubert const char *buf ,
2197276da39aSCy Schubert size_t len )
2198276da39aSCy Schubert {
2199276da39aSCy Schubert /* we're running single threaded with regards to the clocks. */
2200276da39aSCy Schubert static char s_lbuf[2048];
2201276da39aSCy Schubert
2202276da39aSCy Schubert clockprocT * const pp = peer->procptr;
2203276da39aSCy Schubert gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
2204276da39aSCy Schubert
2205*a466cc55SCy Schubert if (debug >= level) {
2206276da39aSCy Schubert const char *sptr = buf;
2207276da39aSCy Schubert const char *stop = buf + len;
2208276da39aSCy Schubert char *dptr = s_lbuf;
2209276da39aSCy Schubert char *dtop = s_lbuf + sizeof(s_lbuf) - 1; /* for NUL */
2210276da39aSCy Schubert
2211276da39aSCy Schubert while (sptr != stop && dptr != dtop) {
22129034852cSGleb Smirnoff u_char uch = (u_char)*sptr++;
22139034852cSGleb Smirnoff if (uch == '\\') {
2214276da39aSCy Schubert dptr = add_string(dptr, dtop, "\\\\");
22159034852cSGleb Smirnoff } else if (isprint(uch)) {
22169034852cSGleb Smirnoff *dptr++ = (char)uch;
2217276da39aSCy Schubert } else {
2218276da39aSCy Schubert char fbuf[6];
22199034852cSGleb Smirnoff snprintf(fbuf, sizeof(fbuf), "\\%03o", uch);
2220276da39aSCy Schubert dptr = add_string(dptr, dtop, fbuf);
2221276da39aSCy Schubert }
2222276da39aSCy Schubert }
2223276da39aSCy Schubert *dptr = '\0';
2224276da39aSCy Schubert mprintf("%s[%s]: '%s'\n", up->logname, what, s_lbuf);
2225276da39aSCy Schubert }
2226276da39aSCy Schubert }
2227276da39aSCy Schubert
2228*a466cc55SCy Schubert
22292b15cb3dSCy Schubert #else
22302b15cb3dSCy Schubert NONEMPTY_TRANSLATION_UNIT
22312b15cb3dSCy Schubert #endif /* REFCLOCK && CLOCK_GPSDJSON */
2232