ntpshmmon.c 9.3 KB

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