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