timehint.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. /*
  2. * timehint.c - put time information in SHM segment for ntpd, or to chrony
  3. *
  4. * Note that for easy debugging all logging from this file is prefixed
  5. * with PPS or NTP.
  6. *
  7. * This file is Copyright 2010 by the GPSD project
  8. * SPDX-License-Identifier: BSD-2-clause
  9. */
  10. #include "gpsd_config.h" /* must be before all includes */
  11. #include <errno.h>
  12. #include <libgen.h>
  13. #include <math.h>
  14. #include <stdbool.h>
  15. #include <string.h>
  16. #include <sys/socket.h>
  17. #include <sys/stat.h>
  18. #include <sys/types.h>
  19. #include <sys/wait.h>
  20. #include <time.h> /* for timespec */
  21. #include <unistd.h>
  22. #include "timespec.h"
  23. #include "gpsd.h"
  24. #include "ntpshm.h"
  25. /* Note: you can start gpsd as non-root, and have it work with ntpd.
  26. * However, it will then only use the ntpshm segments 2 3, and higher.
  27. *
  28. * Ntpd always runs as root (to be able to control the system clock).
  29. * After that it often (depending on its host configuration) drops to run as
  30. * user ntpd and group ntpd.
  31. *
  32. * As of February 2015 its rules for the creation of ntpshm segments are:
  33. *
  34. * Segments 0 and 1: permissions 0600, i.e. other programs can only
  35. * read and write as root.
  36. *
  37. * Segments 2, 3, and higher:
  38. * permissions 0666, i.e. other programs can read
  39. * and write as any user. I.e.: if ntpd has been
  40. * configured to use these segments, any
  41. * unprivileged user is allowed to provide data
  42. * for synchronisation.
  43. *
  44. * By default ntpd creates 0 segments (though the documentation is
  45. * written in such a way as to suggest it creates 4). It can be
  46. * configured to create up to 217. gpsd creates two segments for each
  47. * device it can drive; by default this is 8 segments for 4
  48. * devices,but can be higher if it was compiled with a larger value of
  49. * MAX_DEVICES.
  50. *
  51. * Started as root, gpsd does as ntpd when attaching (creating) the
  52. * segments. In contrast to ntpd, which only attaches (creates)
  53. * configured segments, gpsd creates all segments. Thus a gpsd will
  54. * by default create eight segments 0-7 that an ntpd with default
  55. * configuration does not watch.
  56. *
  57. * Started as non-root, gpsd will only attach (create) segments 2 and
  58. * above, with permissions 0666. As the permissions are for any user,
  59. * the creator does not matter.
  60. *
  61. * For each GPS module gpsd controls, it will use the attached ntpshm
  62. * segments in pairs (for coarse clock and pps source, respectively)
  63. * starting from the first found segments. I.e. started as root, one
  64. * GPS will deliver data on all segments including 0 and 1; started as
  65. * non-root, gpsd will be deliver data only on segments 2 and higher.
  66. *
  67. * Segments are allocated to activated devices on a first-come-first-served
  68. * basis. A device's segment is marked unused when the device is closed and
  69. * may be re-used by devices connected later.
  70. *
  71. * To debug, try looking at the live segments this way:
  72. *
  73. * ipcs -m
  74. *
  75. * results should look like this:
  76. * ------ Shared Memory Segments --------
  77. * key shmid owner perms bytes nattch status
  78. * 0x4e545030 0 root 700 96 2
  79. * 0x4e545031 32769 root 700 96 2
  80. * 0x4e545032 163842 root 666 96 1
  81. * 0x4e545033 196611 root 666 96 1
  82. * 0x4e545034 253555 root 666 96 1
  83. * 0x4e545035 367311 root 666 96 1
  84. *
  85. * For a bit more data try this:
  86. * cat /proc/sysvipc/shm
  87. *
  88. * If gpsd can not open the segments be sure you are not running SELinux
  89. * or apparmor.
  90. *
  91. * if you see the shared segments (keys 1314148400 -- 1314148405), and
  92. * no gpsd or ntpd is running, you can remove them like this:
  93. *
  94. * ipcrm -M 0x4e545030
  95. * ipcrm -M 0x4e545031
  96. * ipcrm -M 0x4e545032
  97. * ipcrm -M 0x4e545033
  98. * ipcrm -M 0x4e545034
  99. * ipcrm -M 0x4e545035
  100. *
  101. * Removing these segments is usually not necessary, as the operating system
  102. * garbage-collects them when they have no attached processes.
  103. */
  104. static volatile struct shmTime *getShmTime(struct gps_context_t *context,
  105. int unit)
  106. {
  107. int shmid;
  108. unsigned int perms;
  109. volatile struct shmTime *p;
  110. // set the SHM perms the way ntpd does
  111. if (unit < 2) {
  112. // we are root, be careful
  113. perms = 0600;
  114. } else {
  115. // we are not root, try to work anyway
  116. perms = 0666;
  117. }
  118. /*
  119. * Note: this call requires root under BSD, and possibly on
  120. * well-secured Linux systems. This is why ntpshm_context_init() has to be
  121. * called before privilege-dropping.
  122. */
  123. shmid = shmget((key_t) (NTPD_BASE + unit),
  124. sizeof(struct shmTime), (int)(IPC_CREAT | perms));
  125. if (shmid == -1) {
  126. GPSD_LOG(LOG_ERROR, &context->errout,
  127. "NTP: shmget(%ld, %zd, %o) fail: %s\n",
  128. (long int)(NTPD_BASE + unit), sizeof(struct shmTime),
  129. (int)perms, strerror(errno));
  130. return NULL;
  131. }
  132. p = (struct shmTime *)shmat(shmid, 0, 0);
  133. if ((int)(long)p == -1) {
  134. GPSD_LOG(LOG_ERROR, &context->errout,
  135. "NTP: shmat failed: %s\n",
  136. strerror(errno));
  137. return NULL;
  138. }
  139. GPSD_LOG(LOG_PROG, &context->errout,
  140. "NTP: shmat(%d,0,0) succeeded, segment %d\n",
  141. shmid, unit);
  142. return p;
  143. }
  144. void ntpshm_context_init(struct gps_context_t *context)
  145. /* Attach all NTP SHM segments. Called once at startup, while still root. */
  146. {
  147. int i;
  148. for (i = 0; i < NTPSHMSEGS; i++) {
  149. // Only grab the first two when running as root.
  150. if (2 <= i || 0 == getuid()) {
  151. context->shmTime[i] = getShmTime(context, i);
  152. }
  153. }
  154. memset(context->shmTimeInuse, 0, sizeof(context->shmTimeInuse));
  155. }
  156. static volatile struct shmTime *ntpshm_alloc(struct gps_context_t *context)
  157. /* allocate NTP SHM segment. return its segment number, or -1 */
  158. {
  159. int i;
  160. for (i = 0; i < NTPSHMSEGS; i++)
  161. if (context->shmTime[i] != NULL && !context->shmTimeInuse[i]) {
  162. context->shmTimeInuse[i] = true;
  163. /*
  164. * In case this segment gets sent to ntpd before an
  165. * ephemeris is available, the LEAP_NOTINSYNC value will
  166. * tell ntpd that this source is in a "clock alarm" state
  167. * and should be ignored. The goal is to prevent ntpd
  168. * from declaring the GPS a falseticker before it gets
  169. * all its marbles together.
  170. */
  171. memset((void *)context->shmTime[i], 0, sizeof(struct shmTime));
  172. context->shmTime[i]->mode = 1;
  173. context->shmTime[i]->leap = LEAP_NOTINSYNC;
  174. context->shmTime[i]->precision = -20;/* initially 1 micro sec */
  175. context->shmTime[i]->nsamples = 3; /* stages of median filter */
  176. return context->shmTime[i];
  177. }
  178. return NULL;
  179. }
  180. static bool ntpshm_free(struct gps_context_t * context,
  181. volatile struct shmTime *s)
  182. /* free NTP SHM segment */
  183. {
  184. int i;
  185. for (i = 0; i < NTPSHMSEGS; i++)
  186. if (s == context->shmTime[i]) {
  187. context->shmTimeInuse[i] = false;
  188. return true;
  189. }
  190. return false;
  191. }
  192. void ntpshm_session_init(struct gps_device_t *session)
  193. {
  194. /* mark NTPD shared memory segments as unused */
  195. session->shm_clock = NULL;
  196. session->shm_pps = NULL;
  197. }
  198. /* put a received fix time into shared memory for NTP */
  199. int ntpshm_put(struct gps_device_t *session, volatile struct shmTime *shmseg,
  200. struct timedelta_t *td)
  201. {
  202. char real_str[TIMESPEC_LEN];
  203. char clock_str[TIMESPEC_LEN];
  204. /* Any NMEA will be about -1 or -2. Garmin GPS-18/USB is around -6 or -7. */
  205. int precision = -20; /* default precision, 1 micro sec */
  206. if (shmseg == NULL) {
  207. GPSD_LOG(LOG_RAW, &session->context->errout, "NTP:PPS: missing shm\n");
  208. return 0;
  209. }
  210. // FIXME: make NMEA precision -1
  211. if (shmseg == session->shm_pps) {
  212. /* precision is a floor so do not make it tight */
  213. if ( source_usb == session->sourcetype ) {
  214. /* if PPS over USB, then precision = -10, 1 milli sec */
  215. precision = -10;
  216. } else {
  217. /* likely PPS over serial, precision = -20, 1 micro sec */
  218. precision = -20;
  219. }
  220. }
  221. ntp_write(shmseg, td, precision, session->context->leap_notify);
  222. GPSD_LOG(LOG_PROG, &session->context->errout,
  223. "NTP: ntpshm_put(%s,%d) %s @ %s\n",
  224. session->gpsdata.dev.path,
  225. precision,
  226. timespec_str(&td->real, real_str, sizeof(real_str)),
  227. timespec_str(&td->clock, clock_str, sizeof(clock_str)));
  228. return 1;
  229. }
  230. #define SOCK_MAGIC 0x534f434b
  231. struct sock_sample {
  232. struct timeval tv;
  233. double offset;
  234. int pulse;
  235. int leap; /* notify that a leap second is upcoming */
  236. // cppcheck-suppress unusedStructMember
  237. int _pad;
  238. int magic; /* must be SOCK_MAGIC */
  239. };
  240. static void init_hook(struct gps_device_t *session)
  241. /* for chrony SOCK interface, which allows nSec timekeeping */
  242. {
  243. /* open the chrony socket */
  244. char chrony_path[GPS_PATH_MAX];
  245. session->chronyfd = -1;
  246. if ( 0 == getuid() ) {
  247. /* this case will fire on command-line devices;
  248. * they're opened before priv-dropping. Matters because
  249. * usually only root can use /run or /var/run.
  250. */
  251. (void)snprintf(chrony_path, sizeof (chrony_path),
  252. RUNDIR "/chrony.%s.sock", basename(session->gpsdata.dev.path));
  253. } else {
  254. (void)snprintf(chrony_path, sizeof (chrony_path),
  255. "/tmp/chrony.%s.sock", basename(session->gpsdata.dev.path));
  256. }
  257. if (access(chrony_path, F_OK) != 0) {
  258. GPSD_LOG(LOG_PROG, &session->context->errout,
  259. "PPS:%s chrony socket %s doesn't exist\n",
  260. session->gpsdata.dev.path, chrony_path);
  261. } else {
  262. session->chronyfd = netlib_localsocket(chrony_path, SOCK_DGRAM);
  263. if (session->chronyfd < 0)
  264. GPSD_LOG(LOG_PROG, &session->context->errout,
  265. "PPS:%s connect chrony socket failed: %s, error: %d, "
  266. "errno: %d/%s\n",
  267. session->gpsdata.dev.path,
  268. chrony_path, session->chronyfd, errno, strerror(errno));
  269. else
  270. GPSD_LOG(LOG_RAW, &session->context->errout,
  271. "PPS:%s using chrony socket: %s\n",
  272. session->gpsdata.dev.path, chrony_path);
  273. }
  274. }
  275. /* td is the real time and clock time of the edge */
  276. /* offset is actual_ts - clock_ts */
  277. static void chrony_send(struct gps_device_t *session, struct timedelta_t *td)
  278. {
  279. char real_str[TIMESPEC_LEN];
  280. char clock_str[TIMESPEC_LEN];
  281. struct sock_sample sample;
  282. struct tm tm;
  283. int leap_notify = session->context->leap_notify;
  284. /*
  285. * insist that leap seconds only happen in june and december
  286. * GPS emits leap pending for 3 months prior to insertion
  287. * NTP expects leap pending for only 1 month prior to insertion
  288. * Per http://bugs.ntp.org/1090
  289. *
  290. * ITU-R TF.460-6, Section 2.1, says leap seconds can be primarily
  291. * in Jun/Dec but may be in March or September
  292. */
  293. (void)gmtime_r( &(td->real.tv_sec), &tm);
  294. if ( 5 != tm.tm_mon && 11 != tm.tm_mon ) {
  295. /* Not june, not December, no way */
  296. leap_notify = LEAP_NOWARNING;
  297. }
  298. /* chrony expects tv-sec since Jan 1970 */
  299. sample.pulse = 0;
  300. sample.leap = leap_notify;
  301. sample.magic = SOCK_MAGIC;
  302. /* chronyd wants a timeval, not a timspec, not to worry, it is
  303. * just the top of the second */
  304. TSTOTV(&sample.tv, &td->clock);
  305. /* calculate the offset as a timespec to not lose precision */
  306. /* if tv_sec greater than 2 then tv_nsec loses precision, but
  307. * not a big deal as slewing will be required */
  308. sample.offset = TS_SUB_D(&td->real, &td->clock);
  309. sample._pad = 0;
  310. GPSD_LOG(LOG_RAW, &session->context->errout,
  311. "PPS chrony_send %s @ %s Offset: %0.9f\n",
  312. timespec_str(&td->real, real_str, sizeof(real_str)),
  313. timespec_str(&td->clock, clock_str, sizeof(clock_str)),
  314. sample.offset);
  315. (void)send(session->chronyfd, &sample, sizeof (sample), 0);
  316. }
  317. static char *report_hook(volatile struct pps_thread_t *pps_thread,
  318. struct timedelta_t *td)
  319. /* ship the time of a PPS event to ntpd and/or chrony */
  320. {
  321. char *log1;
  322. struct gps_device_t *session = (struct gps_device_t *)pps_thread->context;
  323. /* PPS only source never get any serial info
  324. * so no NTPTIME_IS or fixcnt */
  325. if ( source_pps != session->sourcetype) {
  326. /* FIXME! these two validations need to move back into ppsthread.c */
  327. if ( !session->ship_to_ntpd)
  328. return "skipped ship_to_ntp=0";
  329. /*
  330. * Only listen to PPS after several consecutive fixes,
  331. * otherwise time may be inaccurate. We know this is
  332. * required on all Garmin and u-blox. Safest to do it
  333. * for all cases as we have no other general way to know
  334. * if PPS is good.
  335. * Allow override with batteryRTC to allow foot shots.
  336. */
  337. if (false == session->context->batteryRTC &&
  338. session->fixcnt <= NTP_MIN_FIXES &&
  339. (session->gpsdata.set & GOODTIME_IS) == 0)
  340. return "no fix";
  341. }
  342. /* FIXME? how to log socket AND shm reported? */
  343. log1 = "accepted";
  344. if ( 0 <= session->chronyfd ) {
  345. log1 = "accepted chrony sock";
  346. chrony_send(session, td);
  347. }
  348. if (session->shm_pps != NULL)
  349. (void)ntpshm_put(session, session->shm_pps, td);
  350. /* session context might have a hook set, too */
  351. if (session->context->pps_hook != NULL)
  352. session->context->pps_hook(session, td);
  353. return log1;
  354. }
  355. void ntpshm_link_deactivate(struct gps_device_t *session)
  356. /* release ntpshm storage for a session */
  357. {
  358. if (session->shm_clock != NULL) {
  359. (void)ntpshm_free(session->context, session->shm_clock);
  360. session->shm_clock = NULL;
  361. }
  362. if (session->shm_pps != NULL) {
  363. pps_thread_deactivate(&session->pps_thread);
  364. if (session->chronyfd != -1)
  365. (void)close(session->chronyfd);
  366. (void)ntpshm_free(session->context, session->shm_pps);
  367. session->shm_pps = NULL;
  368. }
  369. }
  370. void ntpshm_link_activate(struct gps_device_t *session)
  371. /* set up ntpshm storage for a session */
  372. {
  373. /* don't talk to NTP when we're running inside the test harness */
  374. if (session->sourcetype == source_pty)
  375. return;
  376. if (session->sourcetype != source_pps ) {
  377. /* allocate a shared-memory segment for "NMEA" time data */
  378. session->shm_clock = ntpshm_alloc(session->context);
  379. if (session->shm_clock == NULL) {
  380. GPSD_LOG(LOG_WARN, &session->context->errout,
  381. "NTP: ntpshm_alloc() failed\n");
  382. return;
  383. }
  384. }
  385. if (session->sourcetype == source_usb
  386. || session->sourcetype == source_rs232
  387. || session->sourcetype == source_pps) {
  388. /* We also have the 1pps capability, allocate a shared-memory segment
  389. * for the 1pps time data and launch a thread to capture the 1pps
  390. * transitions
  391. */
  392. if ((session->shm_pps = ntpshm_alloc(session->context)) == NULL) {
  393. GPSD_LOG(LOG_WARN, &session->context->errout,
  394. "PPS: ntpshm_alloc(1) failed\n");
  395. } else {
  396. init_hook(session);
  397. session->pps_thread.report_hook = report_hook;
  398. #ifdef MAGIC_HAT_ENABLE
  399. /*
  400. * The HAT kludge. If we're using the HAT GPS on a
  401. * Raspberry Pi or a workalike like the ODROIDC2, and
  402. * there is a static "first PPS", and we have access because
  403. * we're root, assume we want to use KPPS.
  404. */
  405. if (strcmp(session->pps_thread.devicename, MAGIC_HAT_GPS) == 0
  406. || strcmp(session->pps_thread.devicename,
  407. MAGIC_LINK_GPS) == 0) {
  408. char *first_pps = pps_get_first();
  409. if (access(first_pps, R_OK | W_OK) == 0)
  410. session->pps_thread.devicename = first_pps;
  411. }
  412. #endif /* MAGIC_HAT_ENABLE */
  413. pps_thread_activate(&session->pps_thread);
  414. }
  415. }
  416. }
  417. /* end */
  418. // vim: set expandtab shiftwidth=4