xref: /freebsd/contrib/ntp/ntpd/ntp_ppsdev.c (revision a466cc55)
1*a466cc55SCy Schubert /*
2*a466cc55SCy Schubert  * ntp_ppsdev.c - PPS-device support
3*a466cc55SCy Schubert  *
4*a466cc55SCy Schubert  * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
5*a466cc55SCy Schubert  * The contents of 'html/copyright.html' apply.
6*a466cc55SCy Schubert  * ---------------------------------------------------------------------
7*a466cc55SCy Schubert  * Helper code to work around (or with) a Linux 'specialty': PPS devices
8*a466cc55SCy Schubert  * are created via attaching the PPS line discipline to a TTY.  This
9*a466cc55SCy Schubert  * creates new pps devices, and the PPS API is *not* available through
10*a466cc55SCy Schubert  * the original TTY fd.
11*a466cc55SCy Schubert  *
12*a466cc55SCy Schubert  * Findig the PPS device associated with a TTY is possible but needs
13*a466cc55SCy Schubert  * quite a bit of file system traversal & lookup in the 'sysfs' tree.
14*a466cc55SCy Schubert  *
15*a466cc55SCy Schubert  * The code below does the job for kernel versions 4 & 5, and will
16*a466cc55SCy Schubert  * probably work for older and newer kernels, too... and in any case, if
17*a466cc55SCy Schubert  * the device or symlink to the PPS device with the given name exists,
18*a466cc55SCy Schubert  * it will take precedence anyway.
19*a466cc55SCy Schubert  * ---------------------------------------------------------------------
20*a466cc55SCy Schubert  */
21*a466cc55SCy Schubert #ifdef __linux__
22*a466cc55SCy Schubert # define _GNU_SOURCE
23*a466cc55SCy Schubert #endif
24*a466cc55SCy Schubert 
25*a466cc55SCy Schubert #include "config.h"
26*a466cc55SCy Schubert 
27*a466cc55SCy Schubert #include "ntpd.h"
28*a466cc55SCy Schubert 
29*a466cc55SCy Schubert #ifdef REFCLOCK
30*a466cc55SCy Schubert 
31*a466cc55SCy Schubert #if defined(HAVE_UNISTD_H)
32*a466cc55SCy Schubert # include <unistd.h>
33*a466cc55SCy Schubert #endif
34*a466cc55SCy Schubert #if defined(HAVE_FCNTL_H)
35*a466cc55SCy Schubert # include <fcntl.h>
36*a466cc55SCy Schubert #endif
37*a466cc55SCy Schubert 
38*a466cc55SCy Schubert #include <stdlib.h>
39*a466cc55SCy Schubert 
40*a466cc55SCy Schubert /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
41*a466cc55SCy Schubert #if defined(__linux__) && defined(HAVE_OPENAT) && defined(HAVE_FDOPENDIR)
42*a466cc55SCy Schubert #define WITH_PPSDEV_MATCH
43*a466cc55SCy Schubert /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
44*a466cc55SCy Schubert 
45*a466cc55SCy Schubert #include <stdio.h>
46*a466cc55SCy Schubert #include <dirent.h>
47*a466cc55SCy Schubert #include <string.h>
48*a466cc55SCy Schubert #include <errno.h>
49*a466cc55SCy Schubert 
50*a466cc55SCy Schubert #include <sys/ioctl.h>
51*a466cc55SCy Schubert #include <sys/types.h>
52*a466cc55SCy Schubert #include <sys/stat.h>
53*a466cc55SCy Schubert #include <sys/sysmacros.h>
54*a466cc55SCy Schubert #include <linux/tty.h>
55*a466cc55SCy Schubert 
56*a466cc55SCy Schubert typedef int BOOL;
57*a466cc55SCy Schubert #ifndef TRUE
58*a466cc55SCy Schubert # define TRUE 1
59*a466cc55SCy Schubert #endif
60*a466cc55SCy Schubert #ifndef FALSE
61*a466cc55SCy Schubert # define FALSE 0
62*a466cc55SCy Schubert #endif
63*a466cc55SCy Schubert 
64*a466cc55SCy Schubert static const int OModeF = O_CLOEXEC|O_RDONLY|O_NOCTTY;
65*a466cc55SCy Schubert static const int OModeD = O_CLOEXEC|O_RDONLY|O_DIRECTORY;
66*a466cc55SCy Schubert 
67*a466cc55SCy Schubert /* ------------------------------------------------------------------ */
68*a466cc55SCy Schubert /* extended directory stream
69*a466cc55SCy Schubert  */
70*a466cc55SCy Schubert typedef struct {
71*a466cc55SCy Schubert 	int  dfd;	/* file descriptor for dir for 'openat()' */
72*a466cc55SCy Schubert 	DIR *dir;	/* directory stream for iteration         */
73*a466cc55SCy Schubert } XDIR;
74*a466cc55SCy Schubert 
75*a466cc55SCy Schubert static void
xdirClose(XDIR * pxdir)76*a466cc55SCy Schubert xdirClose(
77*a466cc55SCy Schubert 	XDIR *pxdir)
78*a466cc55SCy Schubert {
79*a466cc55SCy Schubert 	if (NULL != pxdir->dir)
80*a466cc55SCy Schubert 		closedir(pxdir->dir); /* closes the internal FD, too! */
81*a466cc55SCy Schubert 	else if (-1 != pxdir->dfd)
82*a466cc55SCy Schubert 		close(pxdir->dfd);    /* otherwise _we_ have to do it */
83*a466cc55SCy Schubert 	pxdir->dfd = -1;
84*a466cc55SCy Schubert 	pxdir->dir = NULL;
85*a466cc55SCy Schubert }
86*a466cc55SCy Schubert 
87*a466cc55SCy Schubert static BOOL
xdirOpenAt(XDIR * pxdir,int fdo,const char * path)88*a466cc55SCy Schubert xdirOpenAt(
89*a466cc55SCy Schubert 	XDIR       *pxdir,
90*a466cc55SCy Schubert 	int         fdo  ,
91*a466cc55SCy Schubert 	const char *path )
92*a466cc55SCy Schubert {
93*a466cc55SCy Schubert 	/* Officially, the directory stream owns the file discriptor it
94*a466cc55SCy Schubert 	 * received via 'fdopendir()'.  But for the purpose of 'openat()'
95*a466cc55SCy Schubert 	 * it's ok to keep the value around -- even if we should do
96*a466cc55SCy Schubert 	 * _absolutely_nothing_ with it apart from using it as a path
97*a466cc55SCy Schubert 	 * reference!
98*a466cc55SCy Schubert 	 */
99*a466cc55SCy Schubert 	pxdir->dir = NULL;
100*a466cc55SCy Schubert 	if (-1 == (pxdir->dfd = openat(fdo, path, OModeD)))
101*a466cc55SCy Schubert 		goto fail;
102*a466cc55SCy Schubert 	if (NULL == (pxdir->dir = fdopendir(pxdir->dfd)))
103*a466cc55SCy Schubert 		goto fail;
104*a466cc55SCy Schubert 	return TRUE;
105*a466cc55SCy Schubert 
106*a466cc55SCy Schubert   fail:
107*a466cc55SCy Schubert 	xdirClose(pxdir);
108*a466cc55SCy Schubert 	return FALSE;
109*a466cc55SCy Schubert }
110*a466cc55SCy Schubert 
111*a466cc55SCy Schubert /* --------------------------------------------------------------------
112*a466cc55SCy Schubert  * read content of a file (with a size limit) into a piece of allocated
113*a466cc55SCy Schubert  * memory and trim any trailing whitespace.
114*a466cc55SCy Schubert  *
115*a466cc55SCy Schubert  * The issue here is that several files in the 'sysfs' tree claim a size
116*a466cc55SCy Schubert  * of 4096 bytes when you 'stat' them -- but reading gives EOF after a
117*a466cc55SCy Schubert  * few chars.  (I *can* understand why the kernel takes this shortcut.
118*a466cc55SCy Schubert  * it's just a bit unwieldy...)
119*a466cc55SCy Schubert  */
120*a466cc55SCy Schubert static char*
readFileAt(int rfd,const char * path)121*a466cc55SCy Schubert readFileAt(
122*a466cc55SCy Schubert 	int         rfd ,
123*a466cc55SCy Schubert 	const char *path)
124*a466cc55SCy Schubert {
125*a466cc55SCy Schubert 	struct stat sb;
126*a466cc55SCy Schubert 	char *ret = NULL;
127*a466cc55SCy Schubert 	ssize_t rdlen;
128*a466cc55SCy Schubert 	int dfd;
129*a466cc55SCy Schubert 
130*a466cc55SCy Schubert 	if (-1 == (dfd = openat(rfd, path, OModeF)) || -1 == fstat(dfd, &sb))
131*a466cc55SCy Schubert 		goto fail;
132*a466cc55SCy Schubert 	if ((sb.st_size > 0x2000) || (NULL == (ret = malloc(sb.st_size + 1))))
133*a466cc55SCy Schubert 		goto fail;
134*a466cc55SCy Schubert 	if (1 > (rdlen = read(dfd, ret, sb.st_size)))
135*a466cc55SCy Schubert 		goto fail;
136*a466cc55SCy Schubert 	close(dfd);
137*a466cc55SCy Schubert 
138*a466cc55SCy Schubert 	while (rdlen > 0 && ret[rdlen - 1] <= ' ')
139*a466cc55SCy Schubert 		--rdlen;
140*a466cc55SCy Schubert 	ret[rdlen] = '\0';
141*a466cc55SCy Schubert 	return ret;
142*a466cc55SCy Schubert 
143*a466cc55SCy Schubert   fail:
144*a466cc55SCy Schubert 	free(ret);
145*a466cc55SCy Schubert 	if (-1 != dfd)
146*a466cc55SCy Schubert 		close(dfd);
147*a466cc55SCy Schubert 	return NULL;
148*a466cc55SCy Schubert }
149*a466cc55SCy Schubert 
150*a466cc55SCy Schubert /* --------------------------------------------------------------------
151*a466cc55SCy Schubert  * Scan the "/dev" directory for a device with a given major and minor
152*a466cc55SCy Schubert  * device id. Return the path if found.
153*a466cc55SCy Schubert  */
154*a466cc55SCy Schubert static char*
findDevByDevId(dev_t rdev)155*a466cc55SCy Schubert findDevByDevId(
156*a466cc55SCy Schubert 	dev_t rdev)
157*a466cc55SCy Schubert {
158*a466cc55SCy Schubert 	struct stat    sb;
159*a466cc55SCy Schubert 	struct dirent *dent;
160*a466cc55SCy Schubert 	XDIR           xdir;
161*a466cc55SCy Schubert 	char          *name = NULL;
162*a466cc55SCy Schubert 
163*a466cc55SCy Schubert 	if (!xdirOpenAt(&xdir, AT_FDCWD, "/dev"))
164*a466cc55SCy Schubert 		goto done;
165*a466cc55SCy Schubert 
166*a466cc55SCy Schubert 	while (!name && (dent = readdir(xdir.dir))) {
167*a466cc55SCy Schubert 		if (-1 == fstatat(xdir.dfd, dent->d_name,
168*a466cc55SCy Schubert 				  &sb, AT_SYMLINK_NOFOLLOW))
169*a466cc55SCy Schubert 			continue;
170*a466cc55SCy Schubert 		if (!S_ISCHR(sb.st_mode))
171*a466cc55SCy Schubert 			continue;
172*a466cc55SCy Schubert 		if (sb.st_rdev == rdev) {
173*a466cc55SCy Schubert 			if (-1 == asprintf(&name, "/dev/%s", dent->d_name))
174*a466cc55SCy Schubert 				name = NULL;
175*a466cc55SCy Schubert 		}
176*a466cc55SCy Schubert 	}
177*a466cc55SCy Schubert 	xdirClose(&xdir);
178*a466cc55SCy Schubert 
179*a466cc55SCy Schubert   done:
180*a466cc55SCy Schubert 	return name;
181*a466cc55SCy Schubert }
182*a466cc55SCy Schubert 
183*a466cc55SCy Schubert /* --------------------------------------------------------------------
184*a466cc55SCy Schubert  * Get the mofor:minor device id for a character device file descriptor
185*a466cc55SCy Schubert  */
186*a466cc55SCy Schubert static BOOL
getCharDevId(int fd,dev_t * out,struct stat * psb)187*a466cc55SCy Schubert getCharDevId(
188*a466cc55SCy Schubert 	int          fd ,
189*a466cc55SCy Schubert 	dev_t       *out,
190*a466cc55SCy Schubert 	struct stat *psb)
191*a466cc55SCy Schubert {
192*a466cc55SCy Schubert 	BOOL        rc = FALSE;
193*a466cc55SCy Schubert 	struct stat sb;
194*a466cc55SCy Schubert 
195*a466cc55SCy Schubert 	if (NULL == psb)
196*a466cc55SCy Schubert 		psb = &sb;
197*a466cc55SCy Schubert 	if (-1 != fstat(fd, psb)) {
198*a466cc55SCy Schubert 		rc = S_ISCHR(psb->st_mode);
199*a466cc55SCy Schubert 		if (rc)
200*a466cc55SCy Schubert 			*out = psb->st_rdev;
201*a466cc55SCy Schubert 		else
202*a466cc55SCy Schubert 			errno = EINVAL;
203*a466cc55SCy Schubert 	}
204*a466cc55SCy Schubert 	return rc;
205*a466cc55SCy Schubert }
206*a466cc55SCy Schubert 
207*a466cc55SCy Schubert /* --------------------------------------------------------------------
208*a466cc55SCy Schubert  * given the dir-fd of a pps instance dir in the linux sysfs tree, get
209*a466cc55SCy Schubert  * the device IDs for the PPS device and the associated TTY.
210*a466cc55SCy Schubert  */
211*a466cc55SCy Schubert static BOOL
getPpsTuple(int fdDir,dev_t * pTty,dev_t * pPps)212*a466cc55SCy Schubert getPpsTuple(
213*a466cc55SCy Schubert 	int   fdDir,
214*a466cc55SCy Schubert 	dev_t *pTty,
215*a466cc55SCy Schubert 	dev_t *pPps)
216*a466cc55SCy Schubert {
217*a466cc55SCy Schubert 	BOOL          rc = FALSE;
218*a466cc55SCy Schubert 	unsigned long dmaj, dmin;
219*a466cc55SCy Schubert 	struct stat   sb;
220*a466cc55SCy Schubert 	char         *bufp, *endp, *scan;
221*a466cc55SCy Schubert 
222*a466cc55SCy Schubert 	/* 'path' contains the primary path to the associated TTY:
223*a466cc55SCy Schubert 	 * we 'stat()' for the device id in 'st_rdev'.
224*a466cc55SCy Schubert 	 */
225*a466cc55SCy Schubert 	if (NULL == (bufp = readFileAt(fdDir, "path")))
226*a466cc55SCy Schubert 		goto done;
227*a466cc55SCy Schubert 	if ((-1 == stat(bufp, &sb)) || !S_ISCHR(sb.st_mode))
228*a466cc55SCy Schubert 		goto done;
229*a466cc55SCy Schubert 	*pTty = sb.st_rdev;
230*a466cc55SCy Schubert 	free(bufp);
231*a466cc55SCy Schubert 
232*a466cc55SCy Schubert 	/* 'dev' holds the device ID of the PPS device as 'major:minor'
233*a466cc55SCy Schubert 	 * in text format.   *sigh* couldn't that simply be the name of
234*a466cc55SCy Schubert 	 * the PPS device itself, as in 'path' above??? But nooooo....
235*a466cc55SCy Schubert 	 */
236*a466cc55SCy Schubert 	if (NULL == (bufp = readFileAt(fdDir, "dev")))
237*a466cc55SCy Schubert 		goto done;
238*a466cc55SCy Schubert 	dmaj = strtoul((scan = bufp), &endp, 10);
239*a466cc55SCy Schubert 	if ((endp == scan) || (*endp != ':') || (dmaj >= 256))
240*a466cc55SCy Schubert 		goto done;
241*a466cc55SCy Schubert 	dmin = strtoul((scan = endp + 1), &endp, 10);
242*a466cc55SCy Schubert 	if ((endp == scan) || (*endp >= ' ') || (dmin >= 256))
243*a466cc55SCy Schubert 		goto done;
244*a466cc55SCy Schubert 	*pPps = makedev((unsigned int)dmaj, (unsigned int)dmin);
245*a466cc55SCy Schubert 	rc = TRUE;
246*a466cc55SCy Schubert 
247*a466cc55SCy Schubert   done:
248*a466cc55SCy Schubert 	free(bufp);
249*a466cc55SCy Schubert 	return rc;
250*a466cc55SCy Schubert }
251*a466cc55SCy Schubert 
252*a466cc55SCy Schubert /* --------------------------------------------------------------------
253*a466cc55SCy Schubert  * for a given (TTY) device id, lookup the corresponding PPS device id
254*a466cc55SCy Schubert  * by processing the contents of the kernel sysfs tree.
255*a466cc55SCy Schubert  * Returns false if no such PS device can be found; otherwise set the
256*a466cc55SCy Schubert  * ouput parameter to the PPS dev id and return true...
257*a466cc55SCy Schubert  */
258*a466cc55SCy Schubert static BOOL
findPpsDevId(dev_t ttyId,dev_t * pPpsId)259*a466cc55SCy Schubert findPpsDevId(
260*a466cc55SCy Schubert 	dev_t  ttyId ,
261*a466cc55SCy Schubert 	dev_t *pPpsId)
262*a466cc55SCy Schubert {
263*a466cc55SCy Schubert 	BOOL           found = FALSE;
264*a466cc55SCy Schubert 	XDIR           ClassDir;
265*a466cc55SCy Schubert 	struct dirent *dent;
266*a466cc55SCy Schubert 	dev_t          othId, ppsId;
267*a466cc55SCy Schubert 	int            fdDevDir;
268*a466cc55SCy Schubert 
269*a466cc55SCy Schubert 	if (!xdirOpenAt(&ClassDir, AT_FDCWD, "/sys/class/pps"))
270*a466cc55SCy Schubert 		goto done;
271*a466cc55SCy Schubert 
272*a466cc55SCy Schubert 	while (!found && (dent = readdir(ClassDir.dir))) {
273*a466cc55SCy Schubert 
274*a466cc55SCy Schubert 		/* If the entry is not a referring to a PPS device or
275*a466cc55SCy Schubert 		 * if we can't open the directory for reading, skipt it:
276*a466cc55SCy Schubert 		 */
277*a466cc55SCy Schubert 		if (strncmp("pps", dent->d_name, 3))
278*a466cc55SCy Schubert 			continue;
279*a466cc55SCy Schubert 		fdDevDir = openat(ClassDir.dfd, dent->d_name, OModeD);
280*a466cc55SCy Schubert 		if (-1 == fdDevDir)
281*a466cc55SCy Schubert 			continue;
282*a466cc55SCy Schubert 
283*a466cc55SCy Schubert 		/* get the data and check if device ID for the TTY
284*a466cc55SCy Schubert 		 * is what we're looking for:
285*a466cc55SCy Schubert 		 */
286*a466cc55SCy Schubert 		found = getPpsTuple(fdDevDir, &othId, &ppsId)
287*a466cc55SCy Schubert 		    && (ttyId == othId);
288*a466cc55SCy Schubert 		close(fdDevDir);
289*a466cc55SCy Schubert 	}
290*a466cc55SCy Schubert 
291*a466cc55SCy Schubert 	xdirClose(&ClassDir);
292*a466cc55SCy Schubert 
293*a466cc55SCy Schubert 	if (found)
294*a466cc55SCy Schubert 		*pPpsId = ppsId;
295*a466cc55SCy Schubert   done:
296*a466cc55SCy Schubert 	return found;
297*a466cc55SCy Schubert }
298*a466cc55SCy Schubert 
299*a466cc55SCy Schubert /* --------------------------------------------------------------------
300*a466cc55SCy Schubert  * Return the path to a PPS device related to tghe TT fd given. The
301*a466cc55SCy Schubert  * function might even try to instantiate such a PPS device when
302*a466cc55SCy Schubert  * running es effective root.  Returns NULL if no PPS device can be
303*a466cc55SCy Schubert  * established; otherwise it is a 'malloc()'ed area that should be
304*a466cc55SCy Schubert  * 'free()'d after use.
305*a466cc55SCy Schubert  */
306*a466cc55SCy Schubert static char*
findMatchingPpsDev(int fdtty)307*a466cc55SCy Schubert findMatchingPpsDev(
308*a466cc55SCy Schubert 	int fdtty)
309*a466cc55SCy Schubert {
310*a466cc55SCy Schubert 	struct stat sb;
311*a466cc55SCy Schubert 	dev_t       ttyId, ppsId;
312*a466cc55SCy Schubert 	int         fdpps, ldisc = N_PPS;
313*a466cc55SCy Schubert 	char       *dpath = NULL;
314*a466cc55SCy Schubert 
315*a466cc55SCy Schubert 	/* Without the device identifier of the TTY, we're busted: */
316*a466cc55SCy Schubert 	if (!getCharDevId(fdtty, &ttyId, &sb))
317*a466cc55SCy Schubert 		goto done;
318*a466cc55SCy Schubert 
319*a466cc55SCy Schubert 	/* If we find a matching PPS device ID, return the path to the
320*a466cc55SCy Schubert 	 * device. It might not open, but it's the best we can get.
321*a466cc55SCy Schubert 	 */
322*a466cc55SCy Schubert 	if (findPpsDevId(ttyId, &ppsId)) {
323*a466cc55SCy Schubert 		dpath = findDevByDevId(ppsId);
324*a466cc55SCy Schubert 		goto done;
325*a466cc55SCy Schubert 	}
326*a466cc55SCy Schubert 
327*a466cc55SCy Schubert #   ifdef ENABLE_MAGICPPS
328*a466cc55SCy Schubert 	/* 'magic' PPS support -- try to instantiate missing PPS devices
329*a466cc55SCy Schubert 	 * on-the-fly.  Our mileage may vary -- running as root at that
330*a466cc55SCy Schubert 	 * moment is vital for success.  (We *can* create the PPS device
331*a466cc55SCy Schubert 	 * as ordnary user, but we won't be able to open it!)
332*a466cc55SCy Schubert 	 */
333*a466cc55SCy Schubert 
334*a466cc55SCy Schubert 	/* If we're root, try to push the PPS LDISC to the tty FD. If
335*a466cc55SCy Schubert 	 * that does not work out, we're busted again:
336*a466cc55SCy Schubert 	 */
337*a466cc55SCy Schubert 	if ((0 != geteuid()) || (-1 == ioctl(fdtty, TIOCSETD, &ldisc)))
338*a466cc55SCy Schubert 		goto done;
339*a466cc55SCy Schubert 	msyslog(LOG_INFO, "auto-instantiated PPS device for device %u:%u",
340*a466cc55SCy Schubert 		major(ttyId), minor(ttyId));
341*a466cc55SCy Schubert 
342*a466cc55SCy Schubert 	/* We really should find a matching PPS device now. And since
343*a466cc55SCy Schubert 	 * we're root (see above!), we should be able to open that device.
344*a466cc55SCy Schubert 	 */
345*a466cc55SCy Schubert 	if (findPpsDevId(ttyId, &ppsId))
346*a466cc55SCy Schubert 		dpath = findDevByDevId(ppsId);
347*a466cc55SCy Schubert 	if (!dpath)
348*a466cc55SCy Schubert 		goto done;
349*a466cc55SCy Schubert 
350*a466cc55SCy Schubert 	/* And since we're 'root', we might as well try to clone the
351*a466cc55SCy Schubert 	 * ownership and access rights from the original TTY to the
352*a466cc55SCy Schubert 	 * PPS device.  If that does not work, we just have to live with
353*a466cc55SCy Schubert 	 * what we've got so far...
354*a466cc55SCy Schubert 	 */
355*a466cc55SCy Schubert 	if (-1 == (fdpps = open(dpath, OModeF))) {
356*a466cc55SCy Schubert 		msyslog(LOG_ERR, "could not open auto-created '%s': %m", dpath);
357*a466cc55SCy Schubert 		goto done;
358*a466cc55SCy Schubert 	}
359*a466cc55SCy Schubert 	if (-1 == fchmod(fdpps, sb.st_mode)) {
360*a466cc55SCy Schubert 		msyslog(LOG_ERR, "could not chmod auto-created '%s': %m", dpath);
361*a466cc55SCy Schubert 	}
362*a466cc55SCy Schubert 	if (-1 == fchown(fdpps, sb.st_uid, sb.st_gid)) {
363*a466cc55SCy Schubert 		msyslog(LOG_ERR, "could not chown auto-created '%s': %m", dpath);
364*a466cc55SCy Schubert 	}
365*a466cc55SCy Schubert 	close(fdpps);
366*a466cc55SCy Schubert #   else
367*a466cc55SCy Schubert 	(void)ldisc;
368*a466cc55SCy Schubert #   endif
369*a466cc55SCy Schubert 
370*a466cc55SCy Schubert   done:
371*a466cc55SCy Schubert 	/* Whatever we go so far, that's it. */
372*a466cc55SCy Schubert 	return dpath;
373*a466cc55SCy Schubert }
374*a466cc55SCy Schubert 
375*a466cc55SCy Schubert /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
376*a466cc55SCy Schubert #endif /* linux PPS device matcher */
377*a466cc55SCy Schubert /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
378*a466cc55SCy Schubert 
379*a466cc55SCy Schubert #include "ntp_clockdev.h"
380*a466cc55SCy Schubert 
381*a466cc55SCy Schubert int
ppsdev_reopen(const sockaddr_u * srcadr,int ttyfd,int ppsfd,const char * ppspath,int omode,int oflags)382*a466cc55SCy Schubert ppsdev_reopen(
383*a466cc55SCy Schubert 	const sockaddr_u *srcadr,
384*a466cc55SCy Schubert 	int         ttyfd  , /* current tty FD, or -1 */
385*a466cc55SCy Schubert 	int         ppsfd  , /* current pps FD, or -1 */
386*a466cc55SCy Schubert 	const char *ppspath, /* path to pps device, or NULL */
387*a466cc55SCy Schubert 	int         omode  , /* open mode for pps device */
388*a466cc55SCy Schubert 	int         oflags ) /* openn flags for pps device */
389*a466cc55SCy Schubert {
390*a466cc55SCy Schubert 	int retfd = -1;
391*a466cc55SCy Schubert 	const char *altpath;
392*a466cc55SCy Schubert 
393*a466cc55SCy Schubert 	/* avoid 'unused' warnings: we might not use all args, no
394*a466cc55SCy Schubert 	 * thanks to conditional compiling:)
395*a466cc55SCy Schubert 	 */
396*a466cc55SCy Schubert 	(void)ppspath;
397*a466cc55SCy Schubert 	(void)omode;
398*a466cc55SCy Schubert 	(void)oflags;
399*a466cc55SCy Schubert 
400*a466cc55SCy Schubert 	if (NULL != (altpath = clockdev_lookup(srcadr, 1)))
401*a466cc55SCy Schubert 		ppspath = altpath;
402*a466cc55SCy Schubert 
403*a466cc55SCy Schubert #   if defined(__unix__) && !defined(_WIN32)
404*a466cc55SCy Schubert 	if (-1 == retfd) {
405*a466cc55SCy Schubert 		if (ppspath && *ppspath) {
406*a466cc55SCy Schubert 			retfd = open(ppspath, omode, oflags);
407*a466cc55SCy Schubert 			msyslog(LOG_INFO, "ppsdev_open(%s) %s",
408*a466cc55SCy Schubert 				ppspath, (retfd != -1 ? "succeeded" : "failed"));
409*a466cc55SCy Schubert 		}
410*a466cc55SCy Schubert 	}
411*a466cc55SCy Schubert #   endif
412*a466cc55SCy Schubert 
413*a466cc55SCy Schubert #   if defined(WITH_PPSDEV_MATCH)
414*a466cc55SCy Schubert 	if ((-1 == retfd) && (-1 != ttyfd)) {
415*a466cc55SCy Schubert 		char *xpath = findMatchingPpsDev(ttyfd);
416*a466cc55SCy Schubert 		if (xpath && *xpath) {
417*a466cc55SCy Schubert 			retfd = open(xpath, omode, oflags);
418*a466cc55SCy Schubert 			msyslog(LOG_INFO, "ppsdev_open(%s) %s",
419*a466cc55SCy Schubert 				xpath, (retfd != -1 ? "succeeded" : "failed"));
420*a466cc55SCy Schubert 		}
421*a466cc55SCy Schubert 		free(xpath);
422*a466cc55SCy Schubert 	}
423*a466cc55SCy Schubert #   endif
424*a466cc55SCy Schubert 
425*a466cc55SCy Schubert 	/* BSDs and probably SOLARIS can use the TTY fd for the PPS API,
426*a466cc55SCy Schubert 	 * and so does Windows where the PPS API is implemented via an
427*a466cc55SCy Schubert 	 * IOCTL.  Likewise does the 'SoftPPS' implementation in Windows
428*a466cc55SCy Schubert 	 * based on COM Events.  So, if everything else fails, simply
429*a466cc55SCy Schubert 	 * try the FD given for the TTY/COMport...
430*a466cc55SCy Schubert 	 */
431*a466cc55SCy Schubert 	if (-1 == retfd)
432*a466cc55SCy Schubert 		retfd = ppsfd;
433*a466cc55SCy Schubert 	if (-1 == retfd)
434*a466cc55SCy Schubert 		retfd = ttyfd;
435*a466cc55SCy Schubert 
436*a466cc55SCy Schubert 	/* Close the old pps FD, but only if the new pps FD is neither
437*a466cc55SCy Schubert 	 * the tty FD nor the existing pps FD!
438*a466cc55SCy Schubert 	 */
439*a466cc55SCy Schubert 	if ((retfd != ttyfd) && (retfd != ppsfd))
440*a466cc55SCy Schubert 		ppsdev_close(ttyfd, ppsfd);
441*a466cc55SCy Schubert 
442*a466cc55SCy Schubert 	return retfd;
443*a466cc55SCy Schubert }
444*a466cc55SCy Schubert 
445*a466cc55SCy Schubert void
ppsdev_close(int ttyfd,int ppsfd)446*a466cc55SCy Schubert ppsdev_close(
447*a466cc55SCy Schubert 	int ttyfd, /* current tty FD, or -1 */
448*a466cc55SCy Schubert 	int ppsfd) /* current pps FD, or -1 */
449*a466cc55SCy Schubert {
450*a466cc55SCy Schubert 	/* The pps fd might be the same as the tty fd.  We close the pps
451*a466cc55SCy Schubert 	 * channel only if it's valid and _NOT_ the tty itself:
452*a466cc55SCy Schubert 	 */
453*a466cc55SCy Schubert 	if ((-1 != ppsfd) && (ttyfd != ppsfd))
454*a466cc55SCy Schubert 		close(ppsfd);
455*a466cc55SCy Schubert }
456*a466cc55SCy Schubert /* --*-- that's all folks --*-- */
457*a466cc55SCy Schubert #else
458*a466cc55SCy Schubert NONEMPTY_TRANSLATION_UNIT
459*a466cc55SCy Schubert #endif /* !defined(REFCLOCK) */
460