1 /*        $NetBSD: ntp_clockdev.c,v 1.2 2024/08/18 20:47:17 christos Exp $      */
2 
3 /* ntp_clockdev.c - map clock instances to devices
4  *
5  * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
6  * The contents of 'html/copyright.html' apply.
7  * ---------------------------------------------------------------------
8  * The runtime support for the 'device' configuration statement.  Just a
9  * simple list to map refclock source addresses to the device(s) to use
10  * instead of the builtin names.
11  * ---------------------------------------------------------------------
12  */
13 
14 #ifdef HAVE_CONFIG_H
15 # include <config.h>
16 #endif
17 
18 #ifdef HAVE_NETINFO
19 # include <netinfo/ni.h>
20 #endif
21 
22 #include <stdio.h>
23 #include <isc/net.h>
24 
25 #include "ntp.h"
26 #include "ntpd.h"
27 #include "ntp_clockdev.h"
28 
29 /* In the windows port 'refclock_open' is in 'libntp' (windows specific
30  * 'termios.c' source) and calling a function located in NTPD from the
31  * library is not something we should do.  Therefore 'termios.c' now
32  * provides a hook to set a callback function used for the lookup, and
33  * we have to populate that when we have indeed device name
34  * redirections...
35  */
36 #ifdef SYS_WINNT
37 extern const char * (*termios_device_lookup_func)(const sockaddr_u*, int);
38 #endif
39 
40 /* What we remember for a device redirection */
41 typedef struct DeviceInfoS DeviceInfoT;
42 struct DeviceInfoS {
43           DeviceInfoT *next;  /* link to next record                  */
44           int            ident;         /* type (byte1) and unit (byte0)*/
45           char          *ttyName;       /* time data IO device                  */
46           char          *ppsName;       /* PPS device                           */
47 };
48 
49 /* Our list of device redirections: */
50 static DeviceInfoT * InfoList = NULL;
51 
52 /* Free a single record: */
freeDeviceInfo(DeviceInfoT * item)53 static void freeDeviceInfo(
54           DeviceInfoT *item
55           )
56 {
57           if (NULL != item) {
58                     free(item->ttyName);
59                     free(item->ppsName);
60                     free(item);
61           }
62 }
63 
64 /* Get clock ID from pseudo network address. Returns -1 on error. */
65 static int
getClockIdent(const sockaddr_u * srcadr)66 getClockIdent(
67           const sockaddr_u *srcadr
68           )
69 {
70           int clkType, clkUnit;
71 
72           /*
73            * Check for valid address and running peer
74            */
75           if (!ISREFCLOCKADR(srcadr))
76                     return -1;
77 
78           clkType = REFCLOCKTYPE(srcadr);
79           clkUnit = REFCLOCKUNIT(srcadr);
80           return (clkType << 8) + clkUnit;
81 }
82 
83 /* Purge the complete redirection list. */
84 void
clockdev_clear(void)85 clockdev_clear(void)
86 {
87           DeviceInfoT * item;
88           while (NULL != (item = InfoList)) {
89                     InfoList = item->next;
90                     freeDeviceInfo(item);
91           }
92 }
93 
94 /* Remove record(s) for a clock.
95  * returns number of removed records (maybe zero) or -1 on error
96  */
97 int
clockdev_remove(const sockaddr_u * addr_sock)98 clockdev_remove(
99           const sockaddr_u *addr_sock
100           )
101 {
102           DeviceInfoT *item, **ppl;
103           int            rcnt  = 0;
104           const int    ident = getClockIdent(addr_sock);
105 
106           if (ident < 0)
107                     return -1;
108 
109           ppl = &InfoList;
110           while (NULL != (item = *ppl)) {
111                     if (ident == item->ident) {
112                               *ppl = item->next;
113                               freeDeviceInfo(item);
114                               ++rcnt;
115                     } else {
116                               ppl = &item->next;
117                     }
118           }
119           return rcnt;
120 }
121 
122 /* Update or create a redirection record for a clock instace */
123 int /*error*/
clockdev_update(const sockaddr_u * addr_sock,const char * ttyName,const char * ppsName)124 clockdev_update(
125           const sockaddr_u *addr_sock,
126           const char           *ttyName,
127           const char           *ppsName
128           )
129 {
130           DeviceInfoT *item;
131           const int   ident = getClockIdent(addr_sock);
132           if (ident < 0)
133                     return EINVAL;
134 
135           /* make sure Windows can use device redirections, too: */
136 #   ifdef SYS_WINNT
137           termios_device_lookup_func = clockdev_lookup;
138 #   endif
139 
140           /* try to update an existing record */
141           for (item = InfoList;  NULL != item;  item = item->next)
142                     if (ident == item->ident) {
143                               msyslog(LOG_INFO, "Update IO devices for %s: timedata='%s' ppsdata='%s'",
144                                         refnumtoa(addr_sock),
145                                         ttyName ? ttyName : "(null)",
146                                         ppsName ? ppsName : "(null)");
147                               free(item->ttyName);
148                               free(item->ppsName);
149                               item->ttyName = ttyName ? estrdup(ttyName) : NULL;
150                               item->ppsName = ppsName ? estrdup(ppsName) : NULL;
151                               return 0;
152                     }
153 
154           /* seems we have to create a new entry... */
155           msyslog(LOG_INFO, "Add IO devices for %s: timedata='%s' ppsdata='%s'",
156                     refnumtoa(addr_sock),
157                     ttyName ? ttyName : "(null)",
158                     ppsName ? ppsName : "(null)");
159 
160           item = emalloc(sizeof(*item));
161           item->next    = InfoList;
162           item->ident   = ident;
163           item->ttyName = ttyName ? estrdup(ttyName) : NULL;
164           item->ppsName = ppsName ? estrdup(ppsName) : NULL;
165           InfoList = item;
166           return 0;
167 }
168 
169 /* Lookup a redirection for a clock instance. Returns either the name
170  * registered for the device or NULL if no redirection is found.
171  */
172 const char*
clockdev_lookup(const sockaddr_u * addr_sock,int getPPS)173 clockdev_lookup(
174           const sockaddr_u *addr_sock,
175           int                   getPPS
176           )
177 {
178           const DeviceInfoT *item;
179           const int   ident = getClockIdent(addr_sock);
180 
181           if (ident < 0)
182                     return NULL;
183 
184           for (item = InfoList;  NULL != item;  item = item->next)
185                     if (ident == item->ident)
186                               return getPPS ? item->ppsName : item->ttyName;
187 
188           return NULL;
189 }
190