xref: /netbsd-src/external/bsd/ntp/dist/ntpd/ntp_ppsdev.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: ntp_ppsdev.c,v 1.2 2024/08/18 20:47:17 christos Exp $	*/
2897be3a4Schristos 
3897be3a4Schristos /*
4897be3a4Schristos  * ntp_ppsdev.c - PPS-device support
5897be3a4Schristos  *
6897be3a4Schristos  * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
7897be3a4Schristos  * The contents of 'html/copyright.html' apply.
8897be3a4Schristos  * ---------------------------------------------------------------------
9897be3a4Schristos  * Helper code to work around (or with) a Linux 'specialty': PPS devices
10897be3a4Schristos  * are created via attaching the PPS line discipline to a TTY.  This
11897be3a4Schristos  * creates new pps devices, and the PPS API is *not* available through
12897be3a4Schristos  * the original TTY fd.
13897be3a4Schristos  *
14897be3a4Schristos  * Findig the PPS device associated with a TTY is possible but needs
15897be3a4Schristos  * quite a bit of file system traversal & lookup in the 'sysfs' tree.
16897be3a4Schristos  *
17897be3a4Schristos  * The code below does the job for kernel versions 4 & 5, and will
18897be3a4Schristos  * probably work for older and newer kernels, too... and in any case, if
19897be3a4Schristos  * the device or symlink to the PPS device with the given name exists,
20897be3a4Schristos  * it will take precedence anyway.
21897be3a4Schristos  * ---------------------------------------------------------------------
22897be3a4Schristos  */
23897be3a4Schristos #ifdef __linux__
24897be3a4Schristos # define _GNU_SOURCE
25897be3a4Schristos #endif
26897be3a4Schristos 
27897be3a4Schristos #include "config.h"
28897be3a4Schristos 
29897be3a4Schristos #include "ntpd.h"
30897be3a4Schristos 
31897be3a4Schristos #ifdef REFCLOCK
32897be3a4Schristos 
33897be3a4Schristos #if defined(HAVE_UNISTD_H)
34897be3a4Schristos # include <unistd.h>
35897be3a4Schristos #endif
36897be3a4Schristos #if defined(HAVE_FCNTL_H)
37897be3a4Schristos # include <fcntl.h>
38897be3a4Schristos #endif
39897be3a4Schristos 
40897be3a4Schristos #include <stdlib.h>
41897be3a4Schristos 
42897be3a4Schristos /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
43897be3a4Schristos #if defined(__linux__) && defined(HAVE_OPENAT) && defined(HAVE_FDOPENDIR)
44897be3a4Schristos #define WITH_PPSDEV_MATCH
45897be3a4Schristos /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
46897be3a4Schristos 
47897be3a4Schristos #include <stdio.h>
48897be3a4Schristos #include <dirent.h>
49897be3a4Schristos #include <string.h>
50897be3a4Schristos #include <errno.h>
51897be3a4Schristos 
52897be3a4Schristos #include <sys/ioctl.h>
53897be3a4Schristos #include <sys/types.h>
54897be3a4Schristos #include <sys/stat.h>
55897be3a4Schristos #include <sys/sysmacros.h>
56897be3a4Schristos #include <linux/tty.h>
57897be3a4Schristos 
58897be3a4Schristos typedef int BOOL;
59897be3a4Schristos #ifndef TRUE
60897be3a4Schristos # define TRUE 1
61897be3a4Schristos #endif
62897be3a4Schristos #ifndef FALSE
63897be3a4Schristos # define FALSE 0
64897be3a4Schristos #endif
65897be3a4Schristos 
66897be3a4Schristos static const int OModeF = O_CLOEXEC|O_RDONLY|O_NOCTTY;
67897be3a4Schristos static const int OModeD = O_CLOEXEC|O_RDONLY|O_DIRECTORY;
68897be3a4Schristos 
69897be3a4Schristos /* ------------------------------------------------------------------ */
70897be3a4Schristos /* extended directory stream
71897be3a4Schristos  */
72897be3a4Schristos typedef struct {
73897be3a4Schristos 	int  dfd;	/* file descriptor for dir for 'openat()' */
74897be3a4Schristos 	DIR *dir;	/* directory stream for iteration         */
75897be3a4Schristos } XDIR;
76897be3a4Schristos 
77897be3a4Schristos static void
78897be3a4Schristos xdirClose(
79897be3a4Schristos 	XDIR *pxdir)
80897be3a4Schristos {
81897be3a4Schristos 	if (NULL != pxdir->dir)
82897be3a4Schristos 		closedir(pxdir->dir); /* closes the internal FD, too! */
83897be3a4Schristos 	else if (-1 != pxdir->dfd)
84897be3a4Schristos 		close(pxdir->dfd);    /* otherwise _we_ have to do it */
85897be3a4Schristos 	pxdir->dfd = -1;
86897be3a4Schristos 	pxdir->dir = NULL;
87897be3a4Schristos }
88897be3a4Schristos 
89897be3a4Schristos static BOOL
90897be3a4Schristos xdirOpenAt(
91897be3a4Schristos 	XDIR       *pxdir,
92897be3a4Schristos 	int         fdo  ,
93897be3a4Schristos 	const char *path )
94897be3a4Schristos {
95897be3a4Schristos 	/* Officially, the directory stream owns the file discriptor it
96897be3a4Schristos 	 * received via 'fdopendir()'.  But for the purpose of 'openat()'
97897be3a4Schristos 	 * it's ok to keep the value around -- even if we should do
98897be3a4Schristos 	 * _absolutely_nothing_ with it apart from using it as a path
99897be3a4Schristos 	 * reference!
100897be3a4Schristos 	 */
101897be3a4Schristos 	pxdir->dir = NULL;
102897be3a4Schristos 	if (-1 == (pxdir->dfd = openat(fdo, path, OModeD)))
103897be3a4Schristos 		goto fail;
104897be3a4Schristos 	if (NULL == (pxdir->dir = fdopendir(pxdir->dfd)))
105897be3a4Schristos 		goto fail;
106897be3a4Schristos 	return TRUE;
107897be3a4Schristos 
108897be3a4Schristos   fail:
109897be3a4Schristos 	xdirClose(pxdir);
110897be3a4Schristos 	return FALSE;
111897be3a4Schristos }
112897be3a4Schristos 
113897be3a4Schristos /* --------------------------------------------------------------------
114897be3a4Schristos  * read content of a file (with a size limit) into a piece of allocated
115897be3a4Schristos  * memory and trim any trailing whitespace.
116897be3a4Schristos  *
117897be3a4Schristos  * The issue here is that several files in the 'sysfs' tree claim a size
118897be3a4Schristos  * of 4096 bytes when you 'stat' them -- but reading gives EOF after a
119897be3a4Schristos  * few chars.  (I *can* understand why the kernel takes this shortcut.
120897be3a4Schristos  * it's just a bit unwieldy...)
121897be3a4Schristos  */
122897be3a4Schristos static char*
123897be3a4Schristos readFileAt(
124897be3a4Schristos 	int         rfd ,
125897be3a4Schristos 	const char *path)
126897be3a4Schristos {
127897be3a4Schristos 	struct stat sb;
128897be3a4Schristos 	char *ret = NULL;
129897be3a4Schristos 	ssize_t rdlen;
130897be3a4Schristos 	int dfd;
131897be3a4Schristos 
132897be3a4Schristos 	if (-1 == (dfd = openat(rfd, path, OModeF)) || -1 == fstat(dfd, &sb))
133897be3a4Schristos 		goto fail;
134897be3a4Schristos 	if ((sb.st_size > 0x2000) || (NULL == (ret = malloc(sb.st_size + 1))))
135897be3a4Schristos 		goto fail;
136897be3a4Schristos 	if (1 > (rdlen = read(dfd, ret, sb.st_size)))
137897be3a4Schristos 		goto fail;
138897be3a4Schristos 	close(dfd);
139897be3a4Schristos 
140897be3a4Schristos 	while (rdlen > 0 && ret[rdlen - 1] <= ' ')
141897be3a4Schristos 		--rdlen;
142897be3a4Schristos 	ret[rdlen] = '\0';
143897be3a4Schristos 	return ret;
144897be3a4Schristos 
145897be3a4Schristos   fail:
146897be3a4Schristos 	free(ret);
147897be3a4Schristos 	if (-1 != dfd)
148897be3a4Schristos 		close(dfd);
149897be3a4Schristos 	return NULL;
150897be3a4Schristos }
151897be3a4Schristos 
152897be3a4Schristos /* --------------------------------------------------------------------
153897be3a4Schristos  * Scan the "/dev" directory for a device with a given major and minor
154897be3a4Schristos  * device id. Return the path if found.
155897be3a4Schristos  */
156897be3a4Schristos static char*
157897be3a4Schristos findDevByDevId(
158897be3a4Schristos 	dev_t rdev)
159897be3a4Schristos {
160897be3a4Schristos 	struct stat    sb;
161897be3a4Schristos 	struct dirent *dent;
162897be3a4Schristos 	XDIR           xdir;
163897be3a4Schristos 	char          *name = NULL;
164897be3a4Schristos 
165897be3a4Schristos 	if (!xdirOpenAt(&xdir, AT_FDCWD, "/dev"))
166897be3a4Schristos 		goto done;
167897be3a4Schristos 
168897be3a4Schristos 	while (!name && (dent = readdir(xdir.dir))) {
169897be3a4Schristos 		if (-1 == fstatat(xdir.dfd, dent->d_name,
170897be3a4Schristos 				  &sb, AT_SYMLINK_NOFOLLOW))
171897be3a4Schristos 			continue;
172897be3a4Schristos 		if (!S_ISCHR(sb.st_mode))
173897be3a4Schristos 			continue;
174897be3a4Schristos 		if (sb.st_rdev == rdev) {
175897be3a4Schristos 			if (-1 == asprintf(&name, "/dev/%s", dent->d_name))
176897be3a4Schristos 				name = NULL;
177897be3a4Schristos 		}
178897be3a4Schristos 	}
179897be3a4Schristos 	xdirClose(&xdir);
180897be3a4Schristos 
181897be3a4Schristos   done:
182897be3a4Schristos 	return name;
183897be3a4Schristos }
184897be3a4Schristos 
185897be3a4Schristos /* --------------------------------------------------------------------
186897be3a4Schristos  * Get the mofor:minor device id for a character device file descriptor
187897be3a4Schristos  */
188897be3a4Schristos static BOOL
189897be3a4Schristos getCharDevId(
190897be3a4Schristos 	int          fd ,
191897be3a4Schristos 	dev_t       *out,
192897be3a4Schristos 	struct stat *psb)
193897be3a4Schristos {
194897be3a4Schristos 	BOOL        rc = FALSE;
195897be3a4Schristos 	struct stat sb;
196897be3a4Schristos 
197897be3a4Schristos 	if (NULL == psb)
198897be3a4Schristos 		psb = &sb;
199897be3a4Schristos 	if (-1 != fstat(fd, psb)) {
200897be3a4Schristos 		rc = S_ISCHR(psb->st_mode);
201897be3a4Schristos 		if (rc)
202897be3a4Schristos 			*out = psb->st_rdev;
203897be3a4Schristos 		else
204897be3a4Schristos 			errno = EINVAL;
205897be3a4Schristos 	}
206897be3a4Schristos 	return rc;
207897be3a4Schristos }
208897be3a4Schristos 
209897be3a4Schristos /* --------------------------------------------------------------------
210897be3a4Schristos  * given the dir-fd of a pps instance dir in the linux sysfs tree, get
211897be3a4Schristos  * the device IDs for the PPS device and the associated TTY.
212897be3a4Schristos  */
213897be3a4Schristos static BOOL
214897be3a4Schristos getPpsTuple(
215897be3a4Schristos 	int   fdDir,
216897be3a4Schristos 	dev_t *pTty,
217897be3a4Schristos 	dev_t *pPps)
218897be3a4Schristos {
219897be3a4Schristos 	BOOL          rc = FALSE;
220897be3a4Schristos 	unsigned long dmaj, dmin;
221897be3a4Schristos 	struct stat   sb;
222897be3a4Schristos 	char         *bufp, *endp, *scan;
223897be3a4Schristos 
224897be3a4Schristos 	/* 'path' contains the primary path to the associated TTY:
225897be3a4Schristos 	 * we 'stat()' for the device id in 'st_rdev'.
226897be3a4Schristos 	 */
227897be3a4Schristos 	if (NULL == (bufp = readFileAt(fdDir, "path")))
228897be3a4Schristos 		goto done;
229897be3a4Schristos 	if ((-1 == stat(bufp, &sb)) || !S_ISCHR(sb.st_mode))
230897be3a4Schristos 		goto done;
231897be3a4Schristos 	*pTty = sb.st_rdev;
232897be3a4Schristos 	free(bufp);
233897be3a4Schristos 
234897be3a4Schristos 	/* 'dev' holds the device ID of the PPS device as 'major:minor'
235897be3a4Schristos 	 * in text format.   *sigh* couldn't that simply be the name of
236897be3a4Schristos 	 * the PPS device itself, as in 'path' above??? But nooooo....
237897be3a4Schristos 	 */
238897be3a4Schristos 	if (NULL == (bufp = readFileAt(fdDir, "dev")))
239897be3a4Schristos 		goto done;
240897be3a4Schristos 	dmaj = strtoul((scan = bufp), &endp, 10);
241897be3a4Schristos 	if ((endp == scan) || (*endp != ':') || (dmaj >= 256))
242897be3a4Schristos 		goto done;
243897be3a4Schristos 	dmin = strtoul((scan = endp + 1), &endp, 10);
244897be3a4Schristos 	if ((endp == scan) || (*endp >= ' ') || (dmin >= 256))
245897be3a4Schristos 		goto done;
246897be3a4Schristos 	*pPps = makedev((unsigned int)dmaj, (unsigned int)dmin);
247897be3a4Schristos 	rc = TRUE;
248897be3a4Schristos 
249897be3a4Schristos   done:
250897be3a4Schristos 	free(bufp);
251897be3a4Schristos 	return rc;
252897be3a4Schristos }
253897be3a4Schristos 
254897be3a4Schristos /* --------------------------------------------------------------------
255897be3a4Schristos  * for a given (TTY) device id, lookup the corresponding PPS device id
256897be3a4Schristos  * by processing the contents of the kernel sysfs tree.
257897be3a4Schristos  * Returns false if no such PS device can be found; otherwise set the
258897be3a4Schristos  * ouput parameter to the PPS dev id and return true...
259897be3a4Schristos  */
260897be3a4Schristos static BOOL
261897be3a4Schristos findPpsDevId(
262897be3a4Schristos 	dev_t  ttyId ,
263897be3a4Schristos 	dev_t *pPpsId)
264897be3a4Schristos {
265897be3a4Schristos 	BOOL           found = FALSE;
266897be3a4Schristos 	XDIR           ClassDir;
267897be3a4Schristos 	struct dirent *dent;
268897be3a4Schristos 	dev_t          othId, ppsId;
269897be3a4Schristos 	int            fdDevDir;
270897be3a4Schristos 
271897be3a4Schristos 	if (!xdirOpenAt(&ClassDir, AT_FDCWD, "/sys/class/pps"))
272897be3a4Schristos 		goto done;
273897be3a4Schristos 
274897be3a4Schristos 	while (!found && (dent = readdir(ClassDir.dir))) {
275897be3a4Schristos 
276897be3a4Schristos 		/* If the entry is not a referring to a PPS device or
277897be3a4Schristos 		 * if we can't open the directory for reading, skipt it:
278897be3a4Schristos 		 */
279897be3a4Schristos 		if (strncmp("pps", dent->d_name, 3))
280897be3a4Schristos 			continue;
281897be3a4Schristos 		fdDevDir = openat(ClassDir.dfd, dent->d_name, OModeD);
282897be3a4Schristos 		if (-1 == fdDevDir)
283897be3a4Schristos 			continue;
284897be3a4Schristos 
285897be3a4Schristos 		/* get the data and check if device ID for the TTY
286897be3a4Schristos 		 * is what we're looking for:
287897be3a4Schristos 		 */
288897be3a4Schristos 		found = getPpsTuple(fdDevDir, &othId, &ppsId)
289897be3a4Schristos 		    && (ttyId == othId);
290897be3a4Schristos 		close(fdDevDir);
291897be3a4Schristos 	}
292897be3a4Schristos 
293897be3a4Schristos 	xdirClose(&ClassDir);
294897be3a4Schristos 
295897be3a4Schristos 	if (found)
296897be3a4Schristos 		*pPpsId = ppsId;
297897be3a4Schristos   done:
298897be3a4Schristos 	return found;
299897be3a4Schristos }
300897be3a4Schristos 
301897be3a4Schristos /* --------------------------------------------------------------------
302897be3a4Schristos  * Return the path to a PPS device related to tghe TT fd given. The
303897be3a4Schristos  * function might even try to instantiate such a PPS device when
304897be3a4Schristos  * running es effective root.  Returns NULL if no PPS device can be
305897be3a4Schristos  * established; otherwise it is a 'malloc()'ed area that should be
306897be3a4Schristos  * 'free()'d after use.
307897be3a4Schristos  */
308897be3a4Schristos static char*
309897be3a4Schristos findMatchingPpsDev(
310897be3a4Schristos 	int fdtty)
311897be3a4Schristos {
312897be3a4Schristos 	struct stat sb;
313897be3a4Schristos 	dev_t       ttyId, ppsId;
314897be3a4Schristos 	int         fdpps, ldisc = N_PPS;
315897be3a4Schristos 	char       *dpath = NULL;
316897be3a4Schristos 
317897be3a4Schristos 	/* Without the device identifier of the TTY, we're busted: */
318897be3a4Schristos 	if (!getCharDevId(fdtty, &ttyId, &sb))
319897be3a4Schristos 		goto done;
320897be3a4Schristos 
321897be3a4Schristos 	/* If we find a matching PPS device ID, return the path to the
322897be3a4Schristos 	 * device. It might not open, but it's the best we can get.
323897be3a4Schristos 	 */
324897be3a4Schristos 	if (findPpsDevId(ttyId, &ppsId)) {
325897be3a4Schristos 		dpath = findDevByDevId(ppsId);
326897be3a4Schristos 		goto done;
327897be3a4Schristos 	}
328897be3a4Schristos 
329897be3a4Schristos #   ifdef ENABLE_MAGICPPS
330897be3a4Schristos 	/* 'magic' PPS support -- try to instantiate missing PPS devices
331897be3a4Schristos 	 * on-the-fly.  Our mileage may vary -- running as root at that
332897be3a4Schristos 	 * moment is vital for success.  (We *can* create the PPS device
333897be3a4Schristos 	 * as ordnary user, but we won't be able to open it!)
334897be3a4Schristos 	 */
335897be3a4Schristos 
336897be3a4Schristos 	/* If we're root, try to push the PPS LDISC to the tty FD. If
337897be3a4Schristos 	 * that does not work out, we're busted again:
338897be3a4Schristos 	 */
339897be3a4Schristos 	if ((0 != geteuid()) || (-1 == ioctl(fdtty, TIOCSETD, &ldisc)))
340897be3a4Schristos 		goto done;
341897be3a4Schristos 	msyslog(LOG_INFO, "auto-instantiated PPS device for device %u:%u",
342897be3a4Schristos 		major(ttyId), minor(ttyId));
343897be3a4Schristos 
344897be3a4Schristos 	/* We really should find a matching PPS device now. And since
345897be3a4Schristos 	 * we're root (see above!), we should be able to open that device.
346897be3a4Schristos 	 */
347897be3a4Schristos 	if (findPpsDevId(ttyId, &ppsId))
348897be3a4Schristos 		dpath = findDevByDevId(ppsId);
349897be3a4Schristos 	if (!dpath)
350897be3a4Schristos 		goto done;
351897be3a4Schristos 
352897be3a4Schristos 	/* And since we're 'root', we might as well try to clone the
353897be3a4Schristos 	 * ownership and access rights from the original TTY to the
354897be3a4Schristos 	 * PPS device.  If that does not work, we just have to live with
355897be3a4Schristos 	 * what we've got so far...
356897be3a4Schristos 	 */
357897be3a4Schristos 	if (-1 == (fdpps = open(dpath, OModeF))) {
358897be3a4Schristos 		msyslog(LOG_ERR, "could not open auto-created '%s': %m", dpath);
359897be3a4Schristos 		goto done;
360897be3a4Schristos 	}
361897be3a4Schristos 	if (-1 == fchmod(fdpps, sb.st_mode)) {
362897be3a4Schristos 		msyslog(LOG_ERR, "could not chmod auto-created '%s': %m", dpath);
363897be3a4Schristos 	}
364897be3a4Schristos 	if (-1 == fchown(fdpps, sb.st_uid, sb.st_gid)) {
365897be3a4Schristos 		msyslog(LOG_ERR, "could not chown auto-created '%s': %m", dpath);
366897be3a4Schristos 	}
367897be3a4Schristos 	close(fdpps);
368897be3a4Schristos #   else
369897be3a4Schristos 	(void)ldisc;
370897be3a4Schristos #   endif
371897be3a4Schristos 
372897be3a4Schristos   done:
373897be3a4Schristos 	/* Whatever we go so far, that's it. */
374897be3a4Schristos 	return dpath;
375897be3a4Schristos }
376897be3a4Schristos 
377897be3a4Schristos /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
378897be3a4Schristos #endif /* linux PPS device matcher */
379897be3a4Schristos /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
380897be3a4Schristos 
381897be3a4Schristos #include "ntp_clockdev.h"
382897be3a4Schristos 
383897be3a4Schristos int
384897be3a4Schristos ppsdev_reopen(
385897be3a4Schristos 	const sockaddr_u *srcadr,
386897be3a4Schristos 	int         ttyfd  , /* current tty FD, or -1 */
387897be3a4Schristos 	int         ppsfd  , /* current pps FD, or -1 */
388897be3a4Schristos 	const char *ppspath, /* path to pps device, or NULL */
389897be3a4Schristos 	int         omode  , /* open mode for pps device */
390897be3a4Schristos 	int         oflags ) /* openn flags for pps device */
391897be3a4Schristos {
392897be3a4Schristos 	int retfd = -1;
393897be3a4Schristos 	const char *altpath;
394897be3a4Schristos 
395897be3a4Schristos 	/* avoid 'unused' warnings: we might not use all args, no
396897be3a4Schristos 	 * thanks to conditional compiling:)
397897be3a4Schristos 	 */
398897be3a4Schristos 	(void)ppspath;
399897be3a4Schristos 	(void)omode;
400897be3a4Schristos 	(void)oflags;
401897be3a4Schristos 
402897be3a4Schristos 	if (NULL != (altpath = clockdev_lookup(srcadr, 1)))
403897be3a4Schristos 		ppspath = altpath;
404897be3a4Schristos 
405897be3a4Schristos #   if defined(__unix__) && !defined(_WIN32)
406897be3a4Schristos 	if (-1 == retfd) {
407897be3a4Schristos 		if (ppspath && *ppspath) {
408897be3a4Schristos 			retfd = open(ppspath, omode, oflags);
409897be3a4Schristos 			msyslog(LOG_INFO, "ppsdev_open(%s) %s",
410897be3a4Schristos 				ppspath, (retfd != -1 ? "succeeded" : "failed"));
411897be3a4Schristos 		}
412897be3a4Schristos 	}
413897be3a4Schristos #   endif
414897be3a4Schristos 
415897be3a4Schristos #   if defined(WITH_PPSDEV_MATCH)
416897be3a4Schristos 	if ((-1 == retfd) && (-1 != ttyfd)) {
417897be3a4Schristos 		char *xpath = findMatchingPpsDev(ttyfd);
418897be3a4Schristos 		if (xpath && *xpath) {
419897be3a4Schristos 			retfd = open(xpath, omode, oflags);
420897be3a4Schristos 			msyslog(LOG_INFO, "ppsdev_open(%s) %s",
421897be3a4Schristos 				xpath, (retfd != -1 ? "succeeded" : "failed"));
422897be3a4Schristos 		}
423897be3a4Schristos 		free(xpath);
424897be3a4Schristos 	}
425897be3a4Schristos #   endif
426897be3a4Schristos 
427897be3a4Schristos 	/* BSDs and probably SOLARIS can use the TTY fd for the PPS API,
428897be3a4Schristos 	 * and so does Windows where the PPS API is implemented via an
429897be3a4Schristos 	 * IOCTL.  Likewise does the 'SoftPPS' implementation in Windows
430897be3a4Schristos 	 * based on COM Events.  So, if everything else fails, simply
431897be3a4Schristos 	 * try the FD given for the TTY/COMport...
432897be3a4Schristos 	 */
433897be3a4Schristos 	if (-1 == retfd)
434897be3a4Schristos 		retfd = ppsfd;
435897be3a4Schristos 	if (-1 == retfd)
436897be3a4Schristos 		retfd = ttyfd;
437897be3a4Schristos 
438897be3a4Schristos 	/* Close the old pps FD, but only if the new pps FD is neither
439897be3a4Schristos 	 * the tty FD nor the existing pps FD!
440897be3a4Schristos 	 */
441897be3a4Schristos 	if ((retfd != ttyfd) && (retfd != ppsfd))
442897be3a4Schristos 		ppsdev_close(ttyfd, ppsfd);
443897be3a4Schristos 
444897be3a4Schristos 	return retfd;
445897be3a4Schristos }
446897be3a4Schristos 
447897be3a4Schristos void
448897be3a4Schristos ppsdev_close(
449897be3a4Schristos 	int ttyfd, /* current tty FD, or -1 */
450897be3a4Schristos 	int ppsfd) /* current pps FD, or -1 */
451897be3a4Schristos {
452897be3a4Schristos 	/* The pps fd might be the same as the tty fd.  We close the pps
453897be3a4Schristos 	 * channel only if it's valid and _NOT_ the tty itself:
454897be3a4Schristos 	 */
455897be3a4Schristos 	if ((-1 != ppsfd) && (ttyfd != ppsfd))
456897be3a4Schristos 		close(ppsfd);
457897be3a4Schristos }
458897be3a4Schristos /* --*-- that's all folks --*-- */
459897be3a4Schristos #else
460897be3a4Schristos NONEMPTY_TRANSLATION_UNIT
461897be3a4Schristos #endif /* !defined(REFCLOCK) */
462