ntpshmmon.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /* ntpshmmon.c -- monitor the inner end of an ntpshmwrite.connection
  2. *
  3. * This file is Copyright (c) 2010-2018 by the GPSD project
  4. * SPDX-License-Identifier: BSD-2-clause
  5. *
  6. */
  7. #include "gpsd_config.h" /* must be before all includes */
  8. #include <getopt.h>
  9. #include <limits.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h> /* for memset() */
  13. #include <unistd.h>
  14. #include "gps.h" /* for safe_atof() */
  15. #include "ntpshm.h"
  16. #include "revision.h"
  17. #include "timespec.h"
  18. #include "os_compat.h"
  19. #define NTPSEGMENTS 256 /* NTPx for x any byte */
  20. static struct shmTime *segments[NTPSEGMENTS + 1];
  21. int main(int argc, char **argv)
  22. {
  23. int option;
  24. int i;
  25. bool killall = false;
  26. bool offset = false; /* show offset, not seen */
  27. bool verbose = false;
  28. int nsamples = INT_MAX;
  29. time_t timeout = (time_t)0, starttime = time(NULL);
  30. /* a copy of all old segments */
  31. struct shm_stat_t shm_stat_old[NTPSEGMENTS + 1];
  32. char *whoami;
  33. /* strip path from program name */
  34. (whoami = strrchr(argv[0], '/')) ? ++whoami : (whoami = argv[0]);
  35. memset( shm_stat_old, 0 ,sizeof( shm_stat_old));
  36. while ((option = getopt(argc, argv, "?hn:ost:vV")) != -1) {
  37. switch (option) {
  38. case '?':
  39. case 'h':
  40. (void)fprintf(
  41. stderr,
  42. "usage: ntpshmmon [-?] [-h] [-n nsamples] [-o] [-s] [-t nseconds] [-v] [-V]\n"
  43. " -? print this help\n"
  44. " -h print this help\n"
  45. " -n nsamples exit after nsamples\n"
  46. " -o replace Seen@ with Offset\n"
  47. " -s remove SHMs and exit\n"
  48. " -t nseconds exit after nseconds\n"
  49. " -v be verbose\n"
  50. " -V print version and exit\n"
  51. );
  52. exit(EXIT_SUCCESS);
  53. case 'n':
  54. nsamples = atoi(optarg);
  55. break;
  56. case 'o':
  57. offset = true;
  58. break;
  59. case 's':
  60. killall = true;
  61. break;
  62. case 't':
  63. timeout = (time_t)atoi(optarg);
  64. break;
  65. case 'v':
  66. verbose = true;
  67. break;
  68. case 'V':
  69. (void)fprintf(stderr, "%s: version %s (revision %s)\n",
  70. whoami, VERSION, REVISION);
  71. exit(EXIT_SUCCESS);
  72. default:
  73. /* no option, just go and do it */
  74. break;
  75. }
  76. }
  77. /* grab all segments, keep the non-null ones */
  78. for (i = 0; i < NTPSEGMENTS; i++) {
  79. segments[i] = shm_get(i, false, true);
  80. if (verbose && segments[i] != NULL)
  81. (void)fprintf(stderr, "unit %d opened\n", i);
  82. }
  83. if (killall) {
  84. struct shmTime **pp;
  85. for (pp = segments; pp < segments + NTPSEGMENTS; pp++)
  86. if (*pp != NULL)
  87. (void)shmdt((void *)(*pp));
  88. exit(EXIT_SUCCESS);
  89. }
  90. /*
  91. * We want line buffering even if stdout is going to a file. This
  92. * is a (possibly futile) attempt to avoid writing an incomplete
  93. * line on interrupt.
  94. */
  95. setvbuf(stdout, NULL, _IOLBF, 0);
  96. (void)printf("%s: version %s\n", whoami, VERSION);
  97. if (offset) {
  98. (void)printf("# Name Offset Clock Real L Prc\n");
  99. } else {
  100. (void)printf("# Name Seen@ Clock Real L Prc\n");
  101. }
  102. do {
  103. /* the current segment */
  104. struct shm_stat_t shm_stat;
  105. struct timespec delay;
  106. char ts_buf1[TIMESPEC_LEN];
  107. char ts_buf2[TIMESPEC_LEN];
  108. char ts_buf3[TIMESPEC_LEN];
  109. for (i = 0; i < NTPSEGMENTS; i++) {
  110. long long diff; /* 32 bit long is too short for a timespec */
  111. enum segstat_t status = ntp_read(segments[i], &shm_stat, false);
  112. if (verbose)
  113. (void)fprintf(stderr, "unit %d status %d\n", i, status);
  114. switch(status) {
  115. case OK:
  116. /* ntpd can slew the clock at 120% real time
  117. * so do not lock out slightly short cycles
  118. * use 50% of cycle time as lock out limit.
  119. * ignore that system time may jump. */
  120. diff = timespec_diff_ns(shm_stat.tvr, shm_stat_old[i].tvr);
  121. if ( 0 == diff) {
  122. /* no change in tvr */
  123. break;
  124. }
  125. diff = timespec_diff_ns(shm_stat.tvt, shm_stat_old[i].tvt);
  126. if ( 0 == diff) {
  127. /* no change in tvt */
  128. break;
  129. }
  130. /* time stamp it */
  131. clock_gettime(CLOCK_REALTIME, &shm_stat.tvc);
  132. if (offset) {
  133. diff = timespec_diff_ns(shm_stat.tvr, shm_stat.tvt);
  134. printf("sample %s %20.9f %s %s %d %3d\n",
  135. ntp_name(i),
  136. (double)diff * 1e-9,
  137. timespec_str(&shm_stat.tvr, ts_buf1,
  138. sizeof(ts_buf1)),
  139. timespec_str(&shm_stat.tvt, ts_buf2,
  140. sizeof(ts_buf2)),
  141. shm_stat.leap, shm_stat.precision);
  142. } else {
  143. printf("sample %s %s %s %s %d %3d\n",
  144. ntp_name(i),
  145. timespec_str(&shm_stat.tvc, ts_buf1,
  146. sizeof(ts_buf1)),
  147. timespec_str(&shm_stat.tvr, ts_buf2,
  148. sizeof(ts_buf2)),
  149. timespec_str(&shm_stat.tvt, ts_buf3,
  150. sizeof(ts_buf3)),
  151. shm_stat.leap, shm_stat.precision);
  152. }
  153. --nsamples;
  154. /* save the new time stamp */
  155. shm_stat_old[i] = shm_stat; /* structure copy */
  156. break;
  157. case NO_SEGMENT:
  158. break;
  159. case NOT_READY:
  160. /* do nothing, data not ready, wait another cycle */
  161. break;
  162. case BAD_MODE:
  163. (void)fprintf(stderr,
  164. "ntpshmmon: unknown mode %d on segment %s\n",
  165. shm_stat.status, ntp_name(i));
  166. break;
  167. case CLASH:
  168. /* do nothing, data is corrupt, wait another cycle */
  169. break;
  170. default:
  171. (void)fprintf(stderr,
  172. "ntpshmmon: unknown status %d on segment %s\n",
  173. status, ntp_name(i));
  174. break;
  175. }
  176. }
  177. /* all segments now checked */
  178. /*
  179. * Even on a 1 Hz PPS, a sleep(1) may end up
  180. * being sleep(1.1) and missing a beat. Since
  181. * we're ignoring duplicates via timestamp, polling
  182. * at fast intervals should not be a problem
  183. *
  184. * PPS is not always one pulse per second.
  185. * the Garmin GPS 18x-5Hz outputs 5 pulses per second.
  186. * That is a 200 millSec cycle, minimum 20 milliSec duration
  187. * we will wait 1 milliSec out of caution
  188. *
  189. * and, of course, nanosleep() may sleep a lot longer than we ask...
  190. */
  191. if ( timeout ) {
  192. /* do not read time unless it matters */
  193. if ( time(NULL) > (starttime + timeout ) ) {
  194. /* time to exit */
  195. break;
  196. }
  197. }
  198. /* wait 1,000 uSec */
  199. delay.tv_sec = 0;
  200. delay.tv_nsec = 1000000L;
  201. nanosleep(&delay, NULL);
  202. } while ( 0 < nsamples );
  203. exit(EXIT_SUCCESS);
  204. }
  205. /* end */