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