1 | /* |
---|
2 | * refclock_usno - clock driver for the Naval Observatory dialup |
---|
3 | * Michael Shields <shields@tembel.org> 1995/02/25 |
---|
4 | */ |
---|
5 | |
---|
6 | #ifdef HAVE_CONFIG_H |
---|
7 | #include <config.h> |
---|
8 | #endif |
---|
9 | |
---|
10 | #if defined(REFCLOCK) && defined(USNO) |
---|
11 | |
---|
12 | #include <stdio.h> |
---|
13 | #include <ctype.h> |
---|
14 | #include <sys/time.h> |
---|
15 | #ifdef HAVE_SYS_IOCTL_H |
---|
16 | # include <sys/ioctl.h> |
---|
17 | #endif /* HAVE_SYS_IOCTL_H */ |
---|
18 | |
---|
19 | #include "ntpd.h" |
---|
20 | #include "ntp_io.h" |
---|
21 | #include "ntp_unixtime.h" |
---|
22 | #include "ntp_refclock.h" |
---|
23 | #include "ntp_stdlib.h" |
---|
24 | #include "ntp_control.h" |
---|
25 | |
---|
26 | /* |
---|
27 | * This driver supports the Naval Observatory dialup at +1 202 653 0351. |
---|
28 | * It is a hacked-up version of the ACTS driver. |
---|
29 | * |
---|
30 | * This driver does not support the `phone' configuration because that |
---|
31 | * is needlessly global; it would clash with the ACTS driver. |
---|
32 | * |
---|
33 | * The Naval Observatory does not support the echo-delay measurement scheme. |
---|
34 | * |
---|
35 | * However, this driver *does* support UUCP port locking, allowing the |
---|
36 | * line to be shared with other processes when not actually dialing |
---|
37 | * for time. |
---|
38 | */ |
---|
39 | |
---|
40 | /* |
---|
41 | * Interface definitions |
---|
42 | */ |
---|
43 | |
---|
44 | #define DEVICE "/dev/cua%d" /* device name and unit */ |
---|
45 | #define LOCKFILE "/var/lock/LCK..cua%d" |
---|
46 | /* #define LOCKFILE "/usr/spool/uucp/LCK..cua%d" */ |
---|
47 | |
---|
48 | #define PHONE "atdt 202 653 0351" |
---|
49 | /* #define PHONE "atdt 1 202 653 0351" */ |
---|
50 | |
---|
51 | #define SPEED232 B1200 /* uart speed (1200 cowardly baud) */ |
---|
52 | #define PRECISION (-10) /* precision assumed (about 1 ms) */ |
---|
53 | #define REFID "USNO" /* reference ID */ |
---|
54 | #define DESCRIPTION "Naval Observatory dialup" |
---|
55 | |
---|
56 | #define MODE_AUTO 0 /* automatic mode */ |
---|
57 | #define MODE_BACKUP 1 /* backup mode */ |
---|
58 | #define MODE_MANUAL 2 /* manual mode */ |
---|
59 | |
---|
60 | #define MSGCNT 10 /* we need this many time messages */ |
---|
61 | #define SMAX 80 /* max token string length */ |
---|
62 | #define LENCODE 20 /* length of valid timecode string */ |
---|
63 | #define USNO_MINPOLL 10 /* log2 min poll interval (1024 s) */ |
---|
64 | #define USNO_MAXPOLL 14 /* log2 max poll interval (16384 s) */ |
---|
65 | #define MAXOUTAGE 3600 /* max before USNO kicks in (s) */ |
---|
66 | |
---|
67 | /* |
---|
68 | * Modem control strings. These may have to be changed for some modems. |
---|
69 | * |
---|
70 | * AT command prefix |
---|
71 | * B1 initiate call negotiation using Bell 212A |
---|
72 | * &C1 enable carrier detect |
---|
73 | * &D2 hang up and return to command mode on DTR transition |
---|
74 | * E0 modem command echo disabled |
---|
75 | * l1 set modem speaker volume to low level |
---|
76 | * M1 speaker enabled untill carrier detect |
---|
77 | * Q0 return result codes |
---|
78 | * V1 return result codes as English words |
---|
79 | */ |
---|
80 | #define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */ |
---|
81 | #define MODEM_HANGUP "ATH" /* modem disconnect */ |
---|
82 | |
---|
83 | /* |
---|
84 | * Timeouts |
---|
85 | */ |
---|
86 | #define IDLE 60 /* idle timeout (s) */ |
---|
87 | #define WAIT 2 /* wait timeout (s) */ |
---|
88 | #define ANSWER 30 /* answer timeout (s) */ |
---|
89 | #define CONNECT 10 /* connect timeout (s) */ |
---|
90 | #define TIMECODE (MSGCNT+16) /* timecode timeout (s) */ |
---|
91 | |
---|
92 | /* |
---|
93 | * Imported from ntp_timer module |
---|
94 | */ |
---|
95 | extern u_long current_time; /* current time (s) */ |
---|
96 | extern u_long last_time; /* last clock update time (s) */ |
---|
97 | extern struct event timerqueue[]; /* inner space */ |
---|
98 | |
---|
99 | /* |
---|
100 | * Imported from ntpd module |
---|
101 | */ |
---|
102 | extern int debug; /* global debug flag */ |
---|
103 | |
---|
104 | /* |
---|
105 | * Imported from ntp_proto module |
---|
106 | */ |
---|
107 | extern struct peer *sys_peer; /* who is running the show */ |
---|
108 | extern u_char sys_poll; /* log2 of system poll interval */ |
---|
109 | extern struct peer *sys_peer; /* system peer structure pointer */ |
---|
110 | |
---|
111 | /* |
---|
112 | * Unit control structure |
---|
113 | */ |
---|
114 | struct usnounit { |
---|
115 | struct event timer; /* timeout timer */ |
---|
116 | int pollcnt; /* poll message counter */ |
---|
117 | |
---|
118 | int state; /* the first one was Delaware */ |
---|
119 | int run; /* call program run switch */ |
---|
120 | int msgcnt; /* count of time messages received */ |
---|
121 | long redial; /* interval to next automatic call */ |
---|
122 | int unit; /* unit number (= port) */ |
---|
123 | }; |
---|
124 | |
---|
125 | /* |
---|
126 | * Function prototypes |
---|
127 | */ |
---|
128 | static int usno_start P((int, struct peer *)); |
---|
129 | static void usno_shutdown P((int, struct peer *)); |
---|
130 | static void usno_receive P((struct recvbuf *)); |
---|
131 | static void usno_poll P((int, struct peer *)); |
---|
132 | static void usno_timeout P((struct peer *)); |
---|
133 | static void usno_disc P((struct peer *)); |
---|
134 | static int usno_write P((struct peer *, char *)); |
---|
135 | |
---|
136 | /* |
---|
137 | * Transfer vector |
---|
138 | */ |
---|
139 | struct refclock refclock_usno = { |
---|
140 | usno_start, /* start up driver */ |
---|
141 | usno_shutdown, /* shut down driver */ |
---|
142 | usno_poll, /* transmit poll message */ |
---|
143 | noentry, /* not used (usno_control) */ |
---|
144 | noentry, /* not used (usno_init) */ |
---|
145 | noentry, /* not used (usno_buginfo) */ |
---|
146 | NOFLAGS /* not used */ |
---|
147 | }; |
---|
148 | |
---|
149 | |
---|
150 | /* |
---|
151 | * usno_start - open the devices and initialize data for processing |
---|
152 | */ |
---|
153 | static int |
---|
154 | usno_start(unit, peer) |
---|
155 | int unit; |
---|
156 | struct peer *peer; |
---|
157 | { |
---|
158 | register struct usnounit *up; |
---|
159 | struct refclockproc *pp; |
---|
160 | |
---|
161 | /* |
---|
162 | * Initialize miscellaneous variables |
---|
163 | */ |
---|
164 | pp = peer->procptr; |
---|
165 | peer->precision = PRECISION; |
---|
166 | pp->clockdesc = DESCRIPTION; |
---|
167 | memcpy((char *)&pp->refid, REFID, 4); |
---|
168 | peer->minpoll = USNO_MINPOLL; |
---|
169 | peer->maxpoll = USNO_MAXPOLL; |
---|
170 | peer->sstclktype = CTL_SST_TS_TELEPHONE; |
---|
171 | |
---|
172 | /* |
---|
173 | * Allocate and initialize unit structure |
---|
174 | */ |
---|
175 | if (!(up = (struct usnounit *) |
---|
176 | emalloc(sizeof(struct usnounit)))) |
---|
177 | return (0); |
---|
178 | memset((char *)up, 0, sizeof(struct usnounit)); |
---|
179 | up->unit = unit; |
---|
180 | pp->unitptr = (caddr_t)up; |
---|
181 | |
---|
182 | /* |
---|
183 | * Set up the driver timeout |
---|
184 | */ |
---|
185 | up->timer.peer = (struct peer *)peer; |
---|
186 | up->timer.event_handler = usno_timeout; |
---|
187 | up->timer.event_time = current_time + WAIT; |
---|
188 | TIMER_INSERT(timerqueue, &up->timer); |
---|
189 | return (1); |
---|
190 | } |
---|
191 | |
---|
192 | |
---|
193 | /* |
---|
194 | * usno_shutdown - shut down the clock |
---|
195 | */ |
---|
196 | static void |
---|
197 | usno_shutdown(unit, peer) |
---|
198 | int unit; |
---|
199 | struct peer *peer; |
---|
200 | { |
---|
201 | register struct usnounit *up; |
---|
202 | struct refclockproc *pp; |
---|
203 | |
---|
204 | #ifdef DEBUG |
---|
205 | if (debug) |
---|
206 | printf("usno: clock %s shutting down\n", ntoa(&peer->srcadr)); |
---|
207 | #endif |
---|
208 | pp = peer->procptr; |
---|
209 | up = (struct usnounit *)pp->unitptr; |
---|
210 | TIMER_DEQUEUE(&up->timer); |
---|
211 | usno_disc(peer); |
---|
212 | TIMER_DEQUEUE(&up->timer); |
---|
213 | free(up); |
---|
214 | } |
---|
215 | |
---|
216 | |
---|
217 | /* |
---|
218 | * usno_receive - receive data from the serial interface |
---|
219 | */ |
---|
220 | static void |
---|
221 | usno_receive(rbufp) |
---|
222 | struct recvbuf *rbufp; |
---|
223 | { |
---|
224 | register struct usnounit *up; |
---|
225 | struct refclockproc *pp; |
---|
226 | struct peer *peer; |
---|
227 | char str[SMAX]; |
---|
228 | u_fp disp; |
---|
229 | u_long mjd; /* Modified Julian Day */ |
---|
230 | static int day, hour, minute, second; |
---|
231 | |
---|
232 | /* |
---|
233 | * Initialize pointers and read the timecode and timestamp. If |
---|
234 | * the OK modem status code, leave it where folks can find it. |
---|
235 | */ |
---|
236 | peer = (struct peer *)rbufp->recv_srcclock; |
---|
237 | pp = peer->procptr; |
---|
238 | up = (struct usnounit *)pp->unitptr; |
---|
239 | pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, |
---|
240 | &pp->lastrec); |
---|
241 | if (pp->lencode == 0) { |
---|
242 | if (strcmp(pp->a_lastcode, "OK") == 0) |
---|
243 | pp->lencode = 2; |
---|
244 | return; |
---|
245 | } |
---|
246 | #ifdef DEBUG |
---|
247 | if (debug) |
---|
248 | printf("usno: timecode %d %s\n", pp->lencode, |
---|
249 | pp->a_lastcode); |
---|
250 | #endif |
---|
251 | |
---|
252 | switch (up->state) { |
---|
253 | |
---|
254 | case 0: |
---|
255 | |
---|
256 | /* |
---|
257 | * State 0. We are not expecting anything. Probably |
---|
258 | * modem disconnect noise. Go back to sleep. |
---|
259 | */ |
---|
260 | return; |
---|
261 | |
---|
262 | case 1: |
---|
263 | |
---|
264 | /* |
---|
265 | * State 1. We are about to dial. Just drop it. |
---|
266 | */ |
---|
267 | return; |
---|
268 | |
---|
269 | case 2: |
---|
270 | |
---|
271 | /* |
---|
272 | * State 2. We are waiting for the call to be answered. |
---|
273 | * All we care about here is CONNECT as the first token |
---|
274 | * in the string. If the modem signals BUSY, ERROR, NO |
---|
275 | * ANSWER, NO CARRIER or NO DIALTONE, we immediately |
---|
276 | * hang up the phone. If CONNECT doesn't happen after |
---|
277 | * ANSWER seconds, hang up the phone. If everything is |
---|
278 | * okay, start the connect timeout and slide into state |
---|
279 | * 3. |
---|
280 | */ |
---|
281 | (void)strncpy(str, strtok(pp->a_lastcode, " "), SMAX); |
---|
282 | if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") == |
---|
283 | 0 || strcmp(str, "NO") == 0) { |
---|
284 | TIMER_DEQUEUE(&up->timer); |
---|
285 | NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ |
---|
286 | msyslog(LOG_NOTICE, |
---|
287 | "clock %s USNO modem status %s", |
---|
288 | ntoa(&peer->srcadr), pp->a_lastcode); |
---|
289 | usno_disc(peer); |
---|
290 | } else if (strcmp(str, "CONNECT") == 0) { |
---|
291 | TIMER_DEQUEUE(&up->timer); |
---|
292 | up->timer.event_time = current_time + CONNECT; |
---|
293 | TIMER_INSERT(timerqueue, &up->timer); |
---|
294 | up->msgcnt = 0; |
---|
295 | up->state++; |
---|
296 | } else { |
---|
297 | NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ |
---|
298 | msyslog(LOG_WARNING, |
---|
299 | "clock %s USNO unknown modem status %s", |
---|
300 | ntoa(&peer->srcadr), pp->a_lastcode); |
---|
301 | } |
---|
302 | return; |
---|
303 | |
---|
304 | case 3: |
---|
305 | |
---|
306 | /* |
---|
307 | * State 3. The call has been answered and we are |
---|
308 | * waiting for the first message. If this doesn't |
---|
309 | * happen within the timecode timeout, hang up the |
---|
310 | * phone. We probably got a wrong number or they are |
---|
311 | * down. |
---|
312 | */ |
---|
313 | TIMER_DEQUEUE(&up->timer); |
---|
314 | up->timer.event_time = current_time + TIMECODE; |
---|
315 | TIMER_INSERT(timerqueue, &up->timer); |
---|
316 | up->state++; |
---|
317 | return; |
---|
318 | |
---|
319 | case 4: |
---|
320 | |
---|
321 | /* |
---|
322 | * State 4. We are reading a timecode. It's an actual |
---|
323 | * timecode, or it's the `*' OTM. |
---|
324 | * |
---|
325 | * jjjjj nnn hhmmss UTC |
---|
326 | */ |
---|
327 | if (pp->lencode == LENCODE) { |
---|
328 | if (sscanf(pp->a_lastcode, "%5ld %3d %2d%2d%2d UTC", |
---|
329 | &mjd, &day, &hour, &minute, &second) != 5) { |
---|
330 | #ifdef DEBUG |
---|
331 | if (debug) |
---|
332 | printf("usno: bad timecode format\n"); |
---|
333 | #endif |
---|
334 | refclock_report(peer, CEVNT_BADREPLY); |
---|
335 | } else |
---|
336 | up->msgcnt++; |
---|
337 | return; |
---|
338 | } else if (pp->lencode != 1 || !up->msgcnt) |
---|
339 | return; |
---|
340 | /* else, OTM; drop out of switch */ |
---|
341 | } |
---|
342 | |
---|
343 | pp->leap = LEAP_NOWARNING; |
---|
344 | pp->day = day; |
---|
345 | pp->hour = hour; |
---|
346 | pp->minute = minute; |
---|
347 | pp->second = second; |
---|
348 | pp->lasttime = current_time; |
---|
349 | |
---|
350 | /* |
---|
351 | * Colossal hack here. We process each sample in a trimmed-mean |
---|
352 | * filter and determine the reference clock offset and |
---|
353 | * dispersion. The fudge time1 value is added to each sample as |
---|
354 | * received. |
---|
355 | */ |
---|
356 | if (!refclock_process(pp, up->msgcnt, up->msgcnt - up->msgcnt / 3)) { |
---|
357 | #ifdef DEBUG |
---|
358 | if (debug) |
---|
359 | printf("usno: time rejected\n"); |
---|
360 | #endif |
---|
361 | refclock_report(peer, CEVNT_BADTIME); |
---|
362 | return; |
---|
363 | } else if (up->msgcnt < MSGCNT) |
---|
364 | return; |
---|
365 | |
---|
366 | /* |
---|
367 | * We have a filtered sample offset ready for peer processing. |
---|
368 | * We use lastrec as both the reference time and receive time in |
---|
369 | * order to avoid being cute, like setting the reference time |
---|
370 | * later than the receive time, which may cause a paranoid |
---|
371 | * protocol module to chuck out the data. Finaly, we unhook the |
---|
372 | * timeout, arm for the next call, fold the tent and go home. |
---|
373 | */ |
---|
374 | disp = LFPTOFP(&pp->fudgetime2); |
---|
375 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
---|
376 | refclock_receive(peer, &pp->offset, 0, pp->dispersion + |
---|
377 | (u_fp)disp, &pp->lastrec, &pp->lastrec, pp->leap); |
---|
378 | pp->sloppyclockflag &= ~CLK_FLAG1; |
---|
379 | up->pollcnt = 0; |
---|
380 | TIMER_DEQUEUE(&up->timer); |
---|
381 | up->state = 0; |
---|
382 | usno_disc(peer); |
---|
383 | } |
---|
384 | |
---|
385 | |
---|
386 | /* |
---|
387 | * usno_poll - called by the transmit routine |
---|
388 | */ |
---|
389 | static void |
---|
390 | usno_poll(unit, peer) |
---|
391 | int unit; |
---|
392 | struct peer *peer; |
---|
393 | { |
---|
394 | register struct usnounit *up; |
---|
395 | struct refclockproc *pp; |
---|
396 | |
---|
397 | /* |
---|
398 | * If the driver is running, we set the enable flag (fudge |
---|
399 | * flag1), which causes the driver timeout routine to initiate a |
---|
400 | * call. If not, the enable flag can be set using |
---|
401 | * xntpdc. If this is the sustem peer, then follow the system |
---|
402 | * poll interval. |
---|
403 | */ |
---|
404 | pp = peer->procptr; |
---|
405 | up = (struct usnounit *)pp->unitptr; |
---|
406 | if (up->run) { |
---|
407 | pp->sloppyclockflag |= CLK_FLAG1; |
---|
408 | if (peer == sys_peer) |
---|
409 | peer->hpoll = sys_poll; |
---|
410 | else |
---|
411 | peer->hpoll = peer->minpoll; |
---|
412 | } |
---|
413 | } |
---|
414 | |
---|
415 | |
---|
416 | /* |
---|
417 | * usno_timeout - called by the timer interrupt |
---|
418 | */ |
---|
419 | static void |
---|
420 | usno_timeout(peer) |
---|
421 | struct peer *peer; |
---|
422 | { |
---|
423 | register struct usnounit *up; |
---|
424 | struct refclockproc *pp; |
---|
425 | int fd; |
---|
426 | char device[20]; |
---|
427 | char lockfile[128], pidbuf[8]; |
---|
428 | int dtr = TIOCM_DTR; |
---|
429 | |
---|
430 | /* |
---|
431 | * If a timeout occurs in other than state 0, the call has |
---|
432 | * failed. If in state 0, we just see if there is other work to |
---|
433 | * do. |
---|
434 | */ |
---|
435 | pp = peer->procptr; |
---|
436 | up = (struct usnounit *)pp->unitptr; |
---|
437 | if (up->state) { |
---|
438 | if (up->state != 1) { |
---|
439 | usno_disc(peer); |
---|
440 | return; |
---|
441 | } |
---|
442 | /* |
---|
443 | * Call, and start the answer timeout. We think it |
---|
444 | * strange if the OK status has not been received from |
---|
445 | * the modem, but plow ahead anyway. |
---|
446 | * |
---|
447 | * This code is *here* because we had to stick in a brief |
---|
448 | * delay to let the modem settle down after raising DTR, |
---|
449 | * and for the OK to be received. State machines are |
---|
450 | * contorted. |
---|
451 | */ |
---|
452 | if (strcmp(pp->a_lastcode, "OK") != 0) |
---|
453 | NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ |
---|
454 | msyslog(LOG_NOTICE, "clock %s USNO no modem status", |
---|
455 | ntoa(&peer->srcadr)); |
---|
456 | (void)usno_write(peer, PHONE); |
---|
457 | NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ |
---|
458 | msyslog(LOG_NOTICE, "clock %s USNO calling %s\n", |
---|
459 | ntoa(&peer->srcadr), PHONE); |
---|
460 | up->state = 2; |
---|
461 | up->pollcnt++; |
---|
462 | pp->polls++; |
---|
463 | up->timer.event_time = current_time + ANSWER; |
---|
464 | TIMER_INSERT(timerqueue, &up->timer); |
---|
465 | return; |
---|
466 | } |
---|
467 | switch (peer->ttl) { |
---|
468 | |
---|
469 | /* |
---|
470 | * In manual mode the calling program is activated |
---|
471 | * by the xntpdc program using the enable flag (fudge |
---|
472 | * flag1), either manually or by a cron job. |
---|
473 | */ |
---|
474 | case MODE_MANUAL: |
---|
475 | up->run = 0; |
---|
476 | break; |
---|
477 | |
---|
478 | /* |
---|
479 | * In automatic mode the calling program runs |
---|
480 | * continuously at intervals determined by the sys_poll |
---|
481 | * variable. |
---|
482 | */ |
---|
483 | case MODE_AUTO: |
---|
484 | if (!up->run) |
---|
485 | pp->sloppyclockflag |= CLK_FLAG1; |
---|
486 | up->run = 1; |
---|
487 | break; |
---|
488 | |
---|
489 | /* |
---|
490 | * In backup mode the calling program is disabled, |
---|
491 | * unless no system peer has been selected for MAXOUTAGE |
---|
492 | * (3600 s). Once enabled, it runs until some other NTP |
---|
493 | * peer shows up. |
---|
494 | */ |
---|
495 | case MODE_BACKUP: |
---|
496 | if (!up->run && sys_peer == 0) { |
---|
497 | if (current_time - last_time > MAXOUTAGE) { |
---|
498 | up->run = 1; |
---|
499 | peer->hpoll = peer->minpoll; |
---|
500 | NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ |
---|
501 | msyslog(LOG_NOTICE, |
---|
502 | "clock %s USNO backup started ", |
---|
503 | ntoa(&peer->srcadr)); |
---|
504 | } |
---|
505 | } else if (up->run && sys_peer->sstclktype != CTL_SST_TS_TELEPHONE) { |
---|
506 | peer->hpoll = peer->minpoll; |
---|
507 | up->run = 0; |
---|
508 | NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ |
---|
509 | msyslog(LOG_NOTICE, |
---|
510 | "clock %s USNO backup stopped", |
---|
511 | ntoa(&peer->srcadr)); |
---|
512 | } |
---|
513 | break; |
---|
514 | |
---|
515 | default: |
---|
516 | msyslog(LOG_ERR, |
---|
517 | "clock %s USNO invalid mode", ntoa(&peer->srcadr)); |
---|
518 | |
---|
519 | } |
---|
520 | |
---|
521 | /* |
---|
522 | * The fudge flag1 is used as an enable/disable; if set either |
---|
523 | * by the code or via xntpdc, the calling program is |
---|
524 | * started; if reset, the phones stop ringing. |
---|
525 | */ |
---|
526 | if (!(pp->sloppyclockflag & CLK_FLAG1)) { |
---|
527 | up->pollcnt = 0; |
---|
528 | up->timer.event_time = current_time + IDLE; |
---|
529 | TIMER_INSERT(timerqueue, &up->timer); |
---|
530 | return; |
---|
531 | } |
---|
532 | |
---|
533 | /* |
---|
534 | * Lock the port. |
---|
535 | */ |
---|
536 | (void)sprintf(lockfile, LOCKFILE, up->unit); |
---|
537 | fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644); |
---|
538 | if (fd < 0) { |
---|
539 | msyslog(LOG_ERR, "clock %s USNO port busy", |
---|
540 | ntoa(&peer->srcadr)); |
---|
541 | return; |
---|
542 | } |
---|
543 | sprintf(pidbuf, "%d\n", (int) getpid()); |
---|
544 | write(fd, pidbuf, strlen(pidbuf)); |
---|
545 | close(fd); |
---|
546 | |
---|
547 | /* |
---|
548 | * Open serial port. Use ACTS line discipline, if available. It |
---|
549 | * pumps a timestamp into the data stream at every on-time |
---|
550 | * character '*' found. Note: the port must have modem control |
---|
551 | * or deep pockets for the phone bill. HP-UX 9.03 users should |
---|
552 | * have very deep pockets. |
---|
553 | */ |
---|
554 | (void)sprintf(device, DEVICE, up->unit); |
---|
555 | if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS))) { |
---|
556 | unlink(lockfile); |
---|
557 | return; |
---|
558 | } |
---|
559 | if (ioctl(fd, TIOCMBIC, (char *)&dtr) < 0) |
---|
560 | msyslog(LOG_WARNING, "usno_timeout: clock %s: couldn't clear DTR: %m", |
---|
561 | ntoa(&peer->srcadr)); |
---|
562 | |
---|
563 | pp->io.clock_recv = usno_receive; |
---|
564 | pp->io.srcclock = (caddr_t)peer; |
---|
565 | pp->io.datalen = 0; |
---|
566 | pp->io.fd = fd; |
---|
567 | if (!io_addclock(&pp->io)) { |
---|
568 | (void) close(fd); |
---|
569 | unlink(lockfile); |
---|
570 | free(up); |
---|
571 | return; |
---|
572 | } |
---|
573 | |
---|
574 | /* |
---|
575 | * Initialize modem and kill DTR. We skedaddle if this comes |
---|
576 | * bum. |
---|
577 | */ |
---|
578 | if (!usno_write(peer, MODEM_SETUP)) { |
---|
579 | msyslog(LOG_ERR, "clock %s USNO couldn't write", |
---|
580 | ntoa(&peer->srcadr)); |
---|
581 | io_closeclock(&pp->io); |
---|
582 | unlink(lockfile); |
---|
583 | free(up); |
---|
584 | return; |
---|
585 | } |
---|
586 | |
---|
587 | /* |
---|
588 | * Initiate a call to the Observatory. If we wind up here in |
---|
589 | * other than state 0, a successful call could not be completed |
---|
590 | * within minpoll seconds. |
---|
591 | */ |
---|
592 | if (up->pollcnt) { |
---|
593 | refclock_report(peer, CEVNT_TIMEOUT); |
---|
594 | NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ |
---|
595 | msyslog(LOG_NOTICE, |
---|
596 | "clock %s USNO calling program terminated", |
---|
597 | ntoa(&peer->srcadr)); |
---|
598 | pp->sloppyclockflag &= ~CLK_FLAG1; |
---|
599 | up->pollcnt = 0; |
---|
600 | #ifdef DEBUG |
---|
601 | if (debug) |
---|
602 | printf("usno: calling program terminated\n"); |
---|
603 | #endif |
---|
604 | usno_disc(peer); |
---|
605 | return; |
---|
606 | } |
---|
607 | |
---|
608 | /* |
---|
609 | * Raise DTR, and let the modem settle. Then we'll dial. |
---|
610 | */ |
---|
611 | if (ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr) < -1) |
---|
612 | msyslog(LOG_INFO, "usno_timeout: clock %s: couldn't set DTR: %m", |
---|
613 | ntoa(&peer->srcadr)); |
---|
614 | up->state = 1; |
---|
615 | up->timer.event_time = current_time + WAIT; |
---|
616 | TIMER_INSERT(timerqueue, &up->timer); |
---|
617 | } |
---|
618 | |
---|
619 | |
---|
620 | /* |
---|
621 | * usno_disc - disconnect the call and wait for the ruckus to cool |
---|
622 | */ |
---|
623 | static void |
---|
624 | usno_disc(peer) |
---|
625 | struct peer *peer; |
---|
626 | { |
---|
627 | register struct usnounit *up; |
---|
628 | struct refclockproc *pp; |
---|
629 | int dtr = TIOCM_DTR; |
---|
630 | char lockfile[128]; |
---|
631 | |
---|
632 | /* |
---|
633 | * We should never get here other than in state 0, unless a call |
---|
634 | * has timed out. We drop DTR, which will reliably get the modem |
---|
635 | * off the air, even while the modem is hammering away full tilt. |
---|
636 | */ |
---|
637 | pp = peer->procptr; |
---|
638 | up = (struct usnounit *)pp->unitptr; |
---|
639 | |
---|
640 | if (ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr) < 0) |
---|
641 | msyslog(LOG_INFO, "usno_disc: clock %s: couldn't clear DTR: %m", |
---|
642 | ntoa(&peer->srcadr)); |
---|
643 | |
---|
644 | if (up->state > 0) { |
---|
645 | up->state = 0; |
---|
646 | msyslog(LOG_NOTICE, "clock %s USNO call failed %d", |
---|
647 | ntoa(&peer->srcadr), up->state); |
---|
648 | #ifdef DEBUG |
---|
649 | if (debug) |
---|
650 | printf("usno: call failed %d\n", up->state); |
---|
651 | #endif |
---|
652 | } |
---|
653 | |
---|
654 | io_closeclock(&pp->io); |
---|
655 | sprintf(lockfile, LOCKFILE, up->unit); |
---|
656 | unlink(lockfile); |
---|
657 | |
---|
658 | up->timer.event_time = current_time + WAIT; |
---|
659 | TIMER_INSERT(timerqueue, &up->timer); |
---|
660 | } |
---|
661 | |
---|
662 | |
---|
663 | /* |
---|
664 | * usno_write - write a message to the serial port |
---|
665 | */ |
---|
666 | static int |
---|
667 | usno_write(peer, str) |
---|
668 | struct peer *peer; |
---|
669 | char *str; |
---|
670 | { |
---|
671 | register struct usnounit *up; |
---|
672 | struct refclockproc *pp; |
---|
673 | int len; |
---|
674 | int code; |
---|
675 | char cr = '\r'; |
---|
676 | |
---|
677 | /* |
---|
678 | * Not much to do here, other than send the message, handle |
---|
679 | * debug and report faults. |
---|
680 | */ |
---|
681 | pp = peer->procptr; |
---|
682 | up = (struct usnounit *)pp->unitptr; |
---|
683 | len = strlen(str); |
---|
684 | #ifdef DEBUG |
---|
685 | if (debug) |
---|
686 | printf("usno: state %d send %d %s\n", up->state, len, |
---|
687 | str); |
---|
688 | #endif |
---|
689 | code = write(pp->io.fd, str, len) == len; |
---|
690 | code |= write(pp->io.fd, &cr, 1) == 1; |
---|
691 | if (!code) |
---|
692 | refclock_report(peer, CEVNT_FAULT); |
---|
693 | return (code); |
---|
694 | } |
---|
695 | |
---|
696 | #else /* not (REFCLOCK && USNO) */ |
---|
697 | int refclock_usno_bs; |
---|
698 | #endif /* not (REFCLOCK && USNO) */ |
---|