]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
[Bug 2808] GPSD_JSON driver enhancements, step 1.
authorJuergen Perlinger <perlinger@ntp.org>
Wed, 6 May 2015 20:38:12 +0000 (22:38 +0200)
committerJuergen Perlinger <perlinger@ntp.org>
Wed, 6 May 2015 20:38:12 +0000 (22:38 +0200)
 Increase internal token buffer to parse all JSON data, even SKY.
 Avoid syslog clutter when driver has init problems but is nout used later on.

bk: 554a7bb4vjrBpQTDnsyjx-PMmfgujA

ChangeLog
ntpd/refclock_gpsdjson.c

index 3e8634fdc332d0ed53af3537ca54ec860166ea4e..5553782173525f38dc51098f377ac2d10e4edd6c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 ---
 
+* [Bug 2808] GPSD_JSON driver enhancements, step 1.
+  Increase internal token buffer to parse all JSON data, even SKY.
+  Defer logging of errors during driver init until the first unit is
+  started, so the syslog is not cluttered when the driver is not used.
 * CID 1295478: Quiet a pedantic potential error from the fix for Bug 2776.
 * CID 1296235: Fix refclock_jjy.c and correcting type of the driver40-ja.html
 * CID 1269537: Clean up a line of dead code in getShmTime().
index 464cdfb7a3ee33d15fddf35da8ff83446a7def14..ad1027bf2e32e035a1c539c512860ebf51666a3d 100644 (file)
  * This design is a compromise to reduce the IO load for both NTPD and
  * GPSD; it also ensures that data is transmitted and evaluated only
  * once on the side of NTPD.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * trouble shooting hints:
+ *
+ *   Enable and check the clock stats. Check if there are bad replies;
+ *   there should be none. If there are actually bad replies, then the
+ *   driver cannot parse all JSON records from GPSD, and some record
+ *   types are vital for the operation of the driver. This indicates a
+ *   problem on the protocol level.
+ *
+ *   When started on the command line with a debug level >= 2, the
+ *   driver dumps the raw received data and the parser input to
+ *   stdout. Since the debug level is global, NTPD starts to create a
+ *   *lot* of output. It makes sense to pipe it through '(f)grep
+ *   GPSD_JSON' before writing the result to disk.
+ *
+ *   A bit less intrusive is using netcat or telnet to connect to GPSD
+ *   and snoop what NTPD would get. If you try this, you have to send a
+ *   WATCH command to GPSD:
+ *
+ * ?WATCH={"device":"/dev/gps0","enable":true,"json":true,"pps":true};<CRLF>
+ *
+ *   should show you what GPSD has to say to NTPD. Replace "/dev/gps0"
+ *   with the device link used by GPSD, if necessary.
  */
 
+
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
@@ -58,7 +84,8 @@
 #if defined(REFCLOCK) && defined(CLOCK_GPSDJSON) && !defined(SYS_WINNT)
 
 /* =====================================================================
- * get the little JSMN library directly into our guts
+ * Get the little JSMN library directly into our guts. Use the 'parent
+ * link' feature for maximum speed.
  */
 #define JSMN_PARENT_LINKS
 #include "../libjsmn/jsmn.c"
@@ -67,7 +94,7 @@
  * JSON parsing stuff
  */
 
-#define JSMN_MAXTOK    30
+#define JSMN_MAXTOK    350
 #define INVALID_TOKEN (-1)
 
 typedef struct json_ctx {
@@ -364,8 +391,16 @@ static const char * const s_req_watch[2] = {
 static const char * const s_req_version =
     "?VERSION;\r\n";
 
-/* We keep a static list of network addresses for 'localhost:gpsd', and
- * we try to connect to them in round-robin fashion.
+/* We keep a static list of network addresses for 'localhost:gpsd' or a
+ * fallback alias of it, and we try to connect to them in round-robin
+ * fashion. The service lookup is done during the driver init
+ * function to minmise the impact of 'getaddrinfo()'.
+ *
+ * Alas, the init function is called even if there are no clocks
+ * configured for this driver. So it makes sense to defer the logging of
+ * any errors or other notifications until the first clock unit is
+ * started -- otherwise there might be syslog entries from a driver that
+ * is not used at all.
  */
 static addrinfoT  *s_gpsd_addr;
 static gpsd_unitT *s_clock_units;
@@ -378,6 +413,12 @@ static const char * const s_svctab[][2] = {
        { NULL, NULL }
 };
 
+/* list of address resolution errors and index of service entry that
+ * finally worked.
+ */
+static int s_svcerr[sizeof(s_svctab)/sizeof(s_svctab[0])];
+static int s_svcidx;
+
 /* =====================================================================
  * log throttling
  */
@@ -407,6 +448,7 @@ gpsd_init(void)
        addrinfoT   hints;
        int         rc, idx;
 
+       memset(s_svcerr, 0, sizeof(s_svcerr));
        memset(&hints, 0, sizeof(hints));
        hints.ai_family   = AF_UNSPEC;
        hints.ai_protocol = IPPROTO_TCP;
@@ -415,23 +457,49 @@ gpsd_init(void)
        for (idx = 0; s_svctab[idx][0] && !s_gpsd_addr; idx++) {
                rc = getaddrinfo(s_svctab[idx][0], s_svctab[idx][1],
                                 &hints, &s_gpsd_addr);
+               s_svcerr[idx] = rc;
                if (0 == rc)
                        break;
+               s_gpsd_addr = NULL;
+       }
+       s_svcidx = idx;
+}
+
+/* ---------------------------------------------------------------------
+ * Init Check: flush pending log messages and check if we can proceed
+ */
+static int/*BOOL*/
+gpsd_init_check(void)
+{
+       int idx;
+
+       /* Check if there is something to log */
+       if (s_svcidx == 0)
+               return (s_gpsd_addr != NULL);
+
+       /* spool out the resolver errors */
+       for (idx = 0; idx < s_svcidx; ++idx) {
                msyslog(LOG_WARNING,
                        "GPSD_JSON: failed to resolve '%s:%s', rc=%d (%s)",
                        s_svctab[idx][0], s_svctab[idx][1],
-                       rc, gai_strerror(rc));
-               s_gpsd_addr = NULL;
+                       s_svcerr[idx], gai_strerror(s_svcerr[idx]));
        }
 
+       /* check if it was fatal, or if we can proceed */
        if (s_gpsd_addr == NULL)
                msyslog(LOG_ERR, "%s",
                        "GPSD_JSON: failed to get socket address, giving up.");
        else if (idx != 0)
                msyslog(LOG_WARNING,
-                       "GPSD_JSON: using '%s:%s' instead off '%s:%s'",
+                       "GPSD_JSON: using '%s:%s' instead of '%s:%s'",
                        s_svctab[idx][0], s_svctab[idx][1],
                        s_svctab[0][0], s_svctab[0][1]);
+
+       /* make sure this gets logged only once and tell if we can
+        * proceed or not
+        */
+       s_svcidx = 0;
+       return (s_gpsd_addr != NULL);
 }
 
 /* ---------------------------------------------------------------------
@@ -448,6 +516,10 @@ gpsd_start(
 
        struct stat sb;
 
+       /* check if we can proceed at all or if init failed */
+       if ( ! gpsd_init_check())
+               return FALSE;
+
        /* search for matching unit */
        while ((up = *uscan) != NULL && up->unit != (unit & 0x7F))
                uscan = &up->next_unit;
@@ -1672,13 +1744,17 @@ gpsd_parse(
        /* See if we can grab anything potentially useful. JSMN does not
         * need a trailing NUL, but it needs the number of bytes to
         * process. */
-       if (!json_parse_record(&up->json_parse, up->buffer, up->buflen))
+       if (!json_parse_record(&up->json_parse, up->buffer, up->buflen)) {
+               ++up->tc_breply;
                return;
-
+       }
+       
        /* Now dispatch over the objects we know */
        clsid = json_object_lookup_string(&up->json_parse, 0, "class");
-       if (NULL == clsid)
-           return;
+       if (NULL == clsid) {
+               ++up->tc_breply;
+               return;
+       }
 
        if      (!strcmp("TPV", clsid))
                process_tpv(peer, &up->json_parse, rtime);