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