diff options
author | roberto <roberto@FreeBSD.org> | 2008-08-22 15:58:00 +0000 |
---|---|---|
committer | roberto <roberto@FreeBSD.org> | 2008-08-22 15:58:00 +0000 |
commit | b85c7169a740b2edf0106ad59fdaa1b0160f823c (patch) | |
tree | 2b9fb7f64eacb322e95695e412c923e97ba33e88 /contrib/ntp/ntpd/refclock_acts.c | |
parent | 1d197cfe9feac6bc29537d8e53c30b6435937b95 (diff) | |
parent | 7a6072eb585696f8856cd498c3fd194cf49f14c6 (diff) | |
download | FreeBSD-src-b85c7169a740b2edf0106ad59fdaa1b0160f823c.zip FreeBSD-src-b85c7169a740b2edf0106ad59fdaa1b0160f823c.tar.gz |
Merge ntpd & friends 4.2.4p5 from vendor/ntp/dist into head. Next commit
will update usr.sbin/ntp to match this.
MFC after: 2 weeks
Diffstat (limited to 'contrib/ntp/ntpd/refclock_acts.c')
-rw-r--r-- | contrib/ntp/ntpd/refclock_acts.c | 1349 |
1 files changed, 648 insertions, 701 deletions
diff --git a/contrib/ntp/ntpd/refclock_acts.c b/contrib/ntp/ntpd/refclock_acts.c index d26ceed..57f2ca7 100644 --- a/contrib/ntp/ntpd/refclock_acts.c +++ b/contrib/ntp/ntpd/refclock_acts.c @@ -1,8 +1,7 @@ /* - * refclock_acts - clock driver for the NIST/PTB Automated Computer Time - * Service aka Amalgamated Containerized Trash Service (ACTS) + * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time + * Services */ - #ifdef HAVE_CONFIG_H #include <config.h> #endif @@ -22,342 +21,190 @@ # include <sys/ioctl.h> #endif /* HAVE_SYS_IOCTL_H */ -/* MUST BE AFTER LAST #include <config.h> !!! */ - -#if defined(CLOCK_ACTS) && defined(CLOCK_PTBACTS) -# if defined(KEEPPTBACTS) -# undef CLOCK_ACTS -# else /* not KEEPPTBACTS */ -# undef CLOCK_PTBACTS -# endif /* not KEEPPTBACTS */ -#endif /* CLOCK_ACTS && CLOCK_PTBACTS */ - /* - * This driver supports the NIST Automated Computer Time Service (ACTS). - * It periodically dials a prespecified telephone number, receives the - * NIST timecode data and calculates the local clock correction. It is - * designed primarily for use as a backup when neither a radio clock nor - * connectivity to Internet time servers is available. For the best - * accuracy, the individual telephone line/modem delay needs to be - * calibrated using outside sources. - * - * The ACTS is located at NIST Boulder, CO, telephone 303 494 4774. A - * toll call from a residence telephone in Newark, DE, costs between 14 - * and 27 cents, depending on time of day, and from a campus telephone - * between 3 and 4 cents, although it is not clear what carrier and time - * of day discounts apply in this case. The modem dial string will - * differ depending on local telephone configuration, etc., and is - * specified by the phone command in the configuration file. The - * argument to this command is an AT command for a Hayes compatible - * modem. - * - * The accuracy produced by this driver should be in the range of a - * millisecond or two, but may need correction due to the delay - * characteristics of the individual modem involved. For undetermined - * reasons, some modems work with the ACTS echo-delay measurement scheme - * and some don't. This driver tries to do the best it can with what it - * gets. Initial experiments with a Practical Peripherals 9600SA modem - * here in Delaware suggest an accuracy of a millisecond or two can be - * achieved without the scheme by using a fudge time1 value of 65.0 ms. - * In either case, the dispersion for a single call involving ten - * samples is about 1.3 ms. - * - * The driver can operate in either of three modes, as determined by - * the mode parameter in the server configuration command. In mode 0 - * (automatic) the driver operates continuously at intervals depending - * on the prediction error, as measured by the driver, usually in the - * order of several hours. In mode 1 (backup) the driver is enabled in - * automatic mode only when no other source of synchronization is - * available and when more than MAXOUTAGE (3600 s) have elapsed since - * last synchronized by other sources. In mode 2 (manual) the driver - * operates only when enabled using a fudge flags switch, as described - * below. - * - * For reliable call management, this driver requires a 1200-bps modem - * with a Hayes-compatible command set and control over the modem data - * terminal ready (DTR) control line. Present restrictions require the - * use of a POSIX-compatible programming interface, although other - * interfaces may work as well. The modem setup string is hard-coded in - * the driver and may require changes for nonstandard modems or special - * circumstances. - * - * Further information can be found in the README.refclock file in the - * ntp - Version 3 distribution. - * - * Fudge Factors - * - * Ordinarily, the propagation time correction is computed automatically - * by ACTS and the driver. When this is not possible or erratic due to - * individual modem characteristics, the fudge flag2 switch should be - * set to disable the ACTS echo-delay scheme. In any case, the fudge - * time1 parameter can be used to adjust the propagation delay as - * required. - * - * The ACTS call interval is determined in one of three ways. In manual - * mode a call is initiated by setting fudge flag1 using ntpdc, either - * manually or via a cron job. In AUTO mode this flag is set by the peer - * timer, which is controlled by the sys_poll variable in response to - * measured errors. In backup mode the driver is ordinarily asleep, but - * awakes (in auto mode) if all other synchronization sources are lost. - * In either auto or backup modes, the call interval increases as long - * as the measured errors do not exceed the value of the fudge time2 - * parameter. - * - * When the fudge flag1 is set, the ACTS calling program is activated. - * This program dials each number listed in the phones command of the - * configuration file in turn. If a call attempt fails, the next number - * in the list is dialed. The fudge flag1 and counter are reset and the - * calling program terminated if (a) a valid clock update has been - * determined, (b) no more numbers remain in the list, (c) a device - * fault or timeout occurs or (d) fudge flag1 is reset manually using - * ntpdc. - * - * In automatic and backup modes, the driver determines the call - * interval using a procedure depending on the measured prediction - * error and the fudge time2 parameter. If the error exceeds time2 for a - * number of times depending on the current interval, the interval is - * decreased, but not less than about 1000 s. If the error is less than - * time2 for some number of times, the interval is increased, but not - * more than about 18 h. With the default value of zero for fudge time2, - * the interval will increase from 1000 s to the 4000-8000-s range, in - * which the expected accuracy should be in the 1-2-ms range. Setting - * fudge time2 to a large value, like 0.1 s, may result in errors of - * that order, but increase the call interval to the maximum. The exact - * value for each configuration will depend on the modem and operating - * system involved, so some experimentation may be necessary. + * This driver supports the US (NIST, USNO) and European (PTB, NPL, + * etc.) modem time services, as well as Spectracom GPS and WWVB + * receivers connected via a modem. The driver periodically dials a + * number from a telephone list, receives the timecode data and + * calculates the local clock correction. It is designed primarily for + * use as backup when neither a radio clock nor connectivity to Internet + * time servers is available. + * + * This driver requires a modem with a Hayes-compatible command set and + * control over the modem data terminal ready (DTR) control line. The + * modem setup string is hard-coded in the driver and may require + * changes for nonstandard modems or special circumstances. For reasons + * unrelated to this driver, the data set ready (DSR) control line + * should not be set when this driver is first started. + * + * The calling program is initiated by setting fudge flag1, either + * manually or automatically. When flag1 is set, the calling program + * dials the first number in the phone command of the configuration + * file. If that call fails, the calling program dials the second number + * and so on. The number is specified by the Hayes ATDT prefix followed + * by the number itself, including the prefix and long-distance digits + * and delay code, if necessary. The flag1 is reset and the calling + * program terminated if (a) a valid clock update has been determined, + * (b) no more numbers remain in the list, (c) a device fault or timeout + * occurs or (d) fudge flag1 is reset manually. + * + * The driver is transparent to each of the modem time services and + * Spectracom radios. It selects the parsing algorithm depending on the + * message length. There is some hazard should the message be corrupted. + * However, the data format is checked carefully and only if all checks + * succeed is the message accepted. Corrupted lines are discarded + * without complaint. + * + * Fudge controls + * + * flag1 force a call in manual mode + * flag2 enable port locking (not verified) + * flag3 no modem; port is directly connected to device + * flag4 not used + * + * time1 offset adjustment (s) + * + * Ordinarily, the serial port is connected to a modem; however, it can + * be connected directly to a device or another computer for testing and + * calibration. In this case set fudge flag3 and the driver will send a + * single character 'T' at each poll event. In principle, fudge flag2 + * enables port locking, allowing the modem to be shared when not in use + * by this driver. At least on Solaris with the current NTP I/O + * routines, this results only in lots of ugly error messages. */ - /* - * DESCRIPTION OF THE AUTOMATED COMPUTER TELEPHONE SERVICE (ACTS) - * (reformatted from ACTS on-line computer help information) + * National Institute of Science and Technology (NIST) * - * The following is transmitted (at 1200 baud) following completion of - * the telephone connection. + * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii) + * + * Data Format * * National Institute of Standards and Technology * Telephone Time Service, Generator 3B * Enter question mark "?" for HELP * D L D * MJD YR MO DA H M S ST S UT1 msADV <OTM> - * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) * - * 47999 90-04-18 21:39:16 50 0 +.1 045.0 UTC(NIST) * - * 47999 90-04-18 21:39:17 50 0 +.1 045.0 UTC(NIST) * - * 47999 90-04-18 21:39:18 50 0 +.1 045.0 UTC(NIST) * - * 47999 90-04-18 21:39:19 50 0 +.1 037.6 UTC(NIST) # - * 47999 90-04-18 21:39:20 50 0 +.1 037.6 UTC(NIST) # - * etc..etc...etc....... - * - * UTC = Universal Time Coordinated, the official world time referred to - * the zero meridian. + * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF> + * ... * - * DST Daylight savings time characters, valid for the continental - * U.S., are set as follows: + * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is + * the on-time markers echoed by the driver and used by NIST to measure + * and correct for the propagation delay. * - * 00 We are on standard time (ST). - * 01-49 Now on DST, go to ST when your local time is 2:00 am and - * the count is 01. The count is decremented daily at 00 - * (UTC). - * 50 We are on DST. - * 51-99 Now on ST, go to DST when your local time is 2:00 am and - * the count is 51. The count is decremented daily at 00 - * (UTC). + * US Naval Observatory (USNO) * - * The two DST characters provide up to 48 days advance notice of a - * change in time. The count remains at 00 or 50 at other times. + * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO) * - * LS Leap second flag is set to "1" to indicate that a leap second is - * to be added as 23:59:60 (UTC) on the last day of the current UTC - * month. The LS flag will be reset to "0" starting with 23:59:60 - * (UTC). The flag will remain on for the entire month before the - * second is added. Leap seconds are added as needed at the end of - * any month. Usually June and/or December are chosen. + * Data Format (two lines, repeating at one-second intervals) * - * The leap second flag will be set to a "2" to indicate that a - * leap second is to be deleted at 23:59:58--00:00:00 on the last - * day of the current month. (This latter provision is included per - * international recommendation, however it is not likely to be - * required in the near future.) + * jjjjj nnn hhmmss UTC<CR><LF> + * *<CR><LF> * - * DUT1 Approximate difference between earth rotation time (UT1) and - * UTC, in steps of 0.1 second: DUT1 = UT1 - UTC. + * jjjjj modified Julian day number (not used) + * nnn day of year + * hhmmss second of day + * * on-time marker for previous timecode + * ... * - * MJD Modified Julian Date, often used to tag certain scientific data. + * USNO does not correct for the propagation delay. A fudge time1 of + * about .06 s is advisable. * - * The full time format is sent at 1200 baud, 8 bit, 1 stop, no parity. - * The format at 300 Baud is also 8 bit, 1 stop, no parity. At 300 Baud - * the MJD and DUT1 values are deleted and the time is transmitted only - * on even seconds. + * European Services (PTB, NPL, etc.) * - * Maximum on line time will be 56 seconds. If all lines are busy at any - * time, the oldest call will be terminated if it has been on line more - * than 28 seconds, otherwise, the call that first reaches 28 seconds - * will be terminated. + * PTB: +49 531 512038 (Germany) + * NPL: 0906 851 6333 (UK only) * - * Current time is valid at the "on-time" marker (OTM), either "*" or - * "#". The nominal on-time marker (*) will be transmitted 45 ms early - * to account for the 8 ms required to send 1 character at 1200 Baud, - * plus an additional 7 ms for delay from NIST to the user, and - * approximately 30 ms "scrambler" delay inherent in 1200 Baud modems. - * If the caller echoes all characters, NIST will measure the round trip - * delay and advance the on-time marker so that the midpoint of the stop - * bit arrives at the user on time. The amount of msADV will reflect the - * actual required advance in milliseconds and the OTM will be a "#". + * Data format (see the documentation for phone numbers and formats.) * - * (The NIST system requires 4 or 5 consecutive delay measurements which - * are consistent before switching from "*" to "#". If the user has a - * 1200 Baud modem with the same internal delay as that used by NIST, - * then the "#" OTM should arrive at the user within +-2 ms of the - * correct time. + * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF> * - * However, NIST has studied different brands of 1200 Baud modems and - * found internal delays from 24 ms to 40 ms and offsets of the "#" OTM - * of +-10 ms. For many computer users, +-10 ms accuracy should be more - * than adequate since many computer internal clocks can only be set - * with granularity of 20 to 50 ms. In any case, the repeatability of - * the offset for the "#" OTM should be within +-2 ms, if the dial-up - * path is reciprocal and the user doesn't change the brand or model of - * modem used. + * Spectracom GPS and WWVB Receivers * - * This should be true even if the dial-up path on one day is a land- - * line of less than 40 ms (one way) and on the next day is a satellite - * link of 260 to 300 ms. In the rare event that the path is one way by - * satellite and the other way by land line with a round trip - * measurement in the range of 90 to 260 ms, the OTM will remain a "*" - * indicating 45 ms advance. - * - * For user comments write: - * NIST-ACTS - * Time and Frequency Division - * Mail Stop 847 - * 325 Broadway - * Boulder, CO 80303 - * - * Software for setting (PC)DOS compatable machines is available on a - * 360-kbyte diskette for $35.00 from: NIST Office of Standard Reference - * Materials B311-Chemistry Bldg, NIST, Gaithersburg, MD, 20899, (301) - * 975-6776 - * - * PTB timecode service (+49 531 512038) - * The Physikalisch-Technische Bundesanstalt (Germany) - * also supports a modem time service - * as the data formats are very similar this driver can also be compiled for - * utilizing the PTB time code service. - * - * Data format - * 0000000000111111111122222222223333333333444444444455555555556666666666777777777 7 - * 0123456789012345678901234567890123456789012345678901234567890123456789012345678 9 - * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500 * - * A B C D EF G H IJ K L M N O P Q R S T U V W XY Z<CR><LF> - * - * A year - * B month - * C day - * D hour - * E : normally - * A for DST to ST switch first hour - * B for DST to ST switch second hour if not marked in H - * F minute - * G second - * H timezone - * I day of week - * J week of year - * K day of year - * L month for next ST/DST changes - * M day - * N hour - * O UTC year - * P UTC month - * Q UTC day - * R UTC hour - * S UTC minute - * T modified julian day (MJD) - * U DUT1 - * V direction and month if leap second - * W signal delay (assumed/measured) - * X sequence number for additional text line in Y - * Y additional text - * Z on time marker (* - assumed delay / # measured delay) - * <CR>!<LF> ! is second change ! - * - * This format is also used by the National Physical Laboratory (NPL)'s - * TRUETIME service in the UK. In this case the timezone field is - * UTC+0 or UTC+1 for standard and daylight saving time. The phone - * number for this service (a premium rate number) is 0891 516 333. - * It is not clear whether the echo check is implemented. - * - * For more detail, see http://www.npl.co.uk/npl/cetm/taf/truetime.html. + * If a modem is connected to a Spectracom receiver, this driver will + * call it up and retrieve the time in one of two formats. As this + * driver does not send anything, the radio will have to either be + * configured in continuous mode or be polled by another local driver. */ - /* * Interface definitions */ -#define SPEED232 B1200 /* uart speed (1200 cowardly baud) */ +#define DEVICE "/dev/acts%d" /* device name and unit */ +#define SPEED232 B9600 /* uart speed (9600 baud) */ #define PRECISION (-10) /* precision assumed (about 1 ms) */ -#ifdef CLOCK_ACTS -# define REFID "ACTS" /* reference ID */ -# define DESCRIPTION "NIST Automated Computer Time Service" /* WRU */ -# define LENCODE 50 /* length of valid timecode string */ -# define DEVICE "/dev/acts%d" /* device name and unit */ -# define REF_ENTRY refclock_acts -#else /* not CLOCK_ACTS */ -# define REFID "TPTB" /* reference ID */ -# define DESCRIPTION "PTB Automated Computer Time Service" -# define LENCODE 78 /* length of valid timecode string */ -# define DEVICE "/dev/ptb%d" /* device name and unit */ -# define REF_ENTRY refclock_ptb -#endif /* not CLOCK_ACTS */ +#define LOCKFILE "/var/spool/locks/LCK..cua%d" +#define DESCRIPTION "Automated Computer Time Service" /* WRU */ +#define REFID "NONE" /* default reference ID */ +#define MSGCNT 20 /* max message count */ +#define SMAX 256 /* max clockstats line length */ + +/* + * Calling program modes + */ #define MODE_AUTO 0 /* automatic mode */ #define MODE_BACKUP 1 /* backup mode */ #define MODE_MANUAL 2 /* manual mode */ -#define MSGCNT 10 /* we need this many ACTS messages */ -#define SMAX 80 /* max token string length */ -#define ACTS_MINPOLL 10 /* log2 min poll interval (1024 s) */ -#define ACTS_MAXPOLL 18 /* log2 max poll interval (16384 s) */ -#define MAXOUTAGE 3600 /* max before ACTS kicks in (s) */ +/* + * Service identifiers. + */ +#define REFACTS "NIST" /* NIST reference ID */ +#define LENACTS 50 /* NIST format */ +#define REFUSNO "USNO" /* USNO reference ID */ +#define LENUSNO 20 /* USNO */ +#define REFPTB "PTB\0" /* PTB/NPL reference ID */ +#define LENPTB 78 /* PTB/NPL format */ +#define REFWWVB "WWVB" /* WWVB reference ID */ +#define LENWWVB0 22 /* WWVB format 0 */ +#define LENWWVB2 24 /* WWVB format 2 */ +#define LF 0x0a /* ASCII LF */ /* - * Modem control strings. These may have to be changed for some modems. + * Modem setup strings. These may have to be changed for some modems. * * AT command prefix - * B1 initiate call negotiation using Bell 212A - * &C1 enable carrier detect + * B1 US answer tone + * &C0 disable carrier detect * &D2 hang up and return to command mode on DTR transition * E0 modem command echo disabled * l1 set modem speaker volume to low level - * M1 speaker enabled untill carrier detect + * M1 speaker enabled until carrier detect * Q0 return result codes * V1 return result codes as English words */ -#define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */ -#define MODEM_HANGUP "ATH" /* modem disconnect */ +#define MODEM_SETUP "ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */ +#define MODEM_HANGUP "ATH\r" /* modem disconnect */ /* - * Timeouts + * Timeouts (all in seconds) */ -#define IDLE 60 /* idle timeout (s) */ -#define WAIT 2 /* wait timeout (s) */ -#define ANSWER 30 /* answer timeout (s) */ -#define CONNECT 10 /* connect timeout (s) */ -#define TIMECODE 15 /* timecode timeout (s) */ +#define SETUP 3 /* setup timeout */ +#define DTR 1 /* DTR timeout */ +#define ANSWER 60 /* answer timeout */ +#define CONNECT 20 /* first valid message timeout */ +#define TIMECODE 30 /* all valid messages timeout */ /* - * Tables to compute the ddd of year form icky dd/mm timecode. Viva la - * leap. + * State machine codes */ -static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +#define S_IDLE 0 /* wait for poll */ +#define S_OK 1 /* wait for modem setup */ +#define S_DTR 2 /* wait for modem DTR */ +#define S_CONNECT 3 /* wait for answer*/ +#define S_FIRST 4 /* wait for first valid message */ +#define S_MSG 5 /* wait for all messages */ +#define S_CLOSE 6 /* wait after sending disconnect */ /* * Unit control structure */ struct actsunit { - int pollcnt; /* poll message counter */ + int unit; /* unit number */ int state; /* the first one was Delaware */ - int run; /* call program run switch */ - int msgcnt; /* count of ACTS messages received */ - long redial; /* interval to next automatic call */ - double msADV; /* millisecond advance of last message */ + int timer; /* timeout counter */ + int retry; /* retry index */ + int msgcnt; /* count of messages received */ + l_fp tstamp; /* on-time timestamp */ + char *bufptr; /* buffer pointer */ }; /* @@ -366,77 +213,54 @@ struct actsunit { static int acts_start P((int, struct peer *)); static void acts_shutdown P((int, struct peer *)); static void acts_receive P((struct recvbuf *)); +static void acts_message P((struct peer *)); +static void acts_timecode P((struct peer *, char *)); static void acts_poll P((int, struct peer *)); static void acts_timeout P((struct peer *)); static void acts_disc P((struct peer *)); -static int acts_write P((struct peer *, const char *)); +static void acts_timer P((int, struct peer *)); /* * Transfer vector (conditional structure name) */ -struct refclock REF_ENTRY = { +struct refclock refclock_acts = { acts_start, /* start up driver */ acts_shutdown, /* shut down driver */ acts_poll, /* transmit poll message */ - noentry, /* not used (old acts_control) */ - noentry, /* not used (old acts_init) */ - noentry, /* not used (old acts_buginfo) */ - NOFLAGS /* not used */ + noentry, /* not used */ + noentry, /* not used */ + noentry, /* not used */ + acts_timer /* housekeeping timer */ }; +struct refclock refclock_ptb; /* - * acts_start - open the devices and initialize data for processing + * Initialize data for processing */ - static int acts_start ( - int unit, + int unit, struct peer *peer ) { - register struct actsunit *up; + struct actsunit *up; struct refclockproc *pp; - int fd; - char device[20]; - int dtr = TIOCM_DTR; - - /* - * Open serial port. Use ACTS line discipline, if available. It - * pumps a timestamp into the data stream at every on-time - * character '*' found. Note: the port must have modem control - * or deep pockets for the phone bill. HP-UX 9.03 users should - * have very deep pockets. - */ - (void)sprintf(device, DEVICE, unit); - if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS))) - return (0); - if (ioctl(fd, TIOCMBIS, (char *)&dtr) < 0) { - msyslog(LOG_ERR, "clock %s ACTS no modem control", - ntoa(&peer->srcadr)); - return (0); - } /* * Allocate and initialize unit structure */ - if (!(up = (struct actsunit *) - emalloc(sizeof(struct actsunit)))) { - (void) close(fd); + up = emalloc(sizeof(struct actsunit)); + if (up == NULL) return (0); - } - memset((char *)up, 0, sizeof(struct actsunit)); + + memset(up, 0, sizeof(struct actsunit)); + up->unit = unit; pp = peer->procptr; + pp->unitptr = (caddr_t)up; pp->io.clock_recv = acts_receive; pp->io.srcclock = (caddr_t)peer; pp->io.datalen = 0; - pp->io.fd = fd; - if (!io_addclock(&pp->io)) { - (void) close(fd); - free(up); - return (0); - } - pp->unitptr = (caddr_t)up; /* * Initialize miscellaneous variables @@ -444,24 +268,9 @@ acts_start ( peer->precision = PRECISION; pp->clockdesc = DESCRIPTION; memcpy((char *)&pp->refid, REFID, 4); - peer->minpoll = ACTS_MINPOLL; - peer->maxpoll = ACTS_MAXPOLL; peer->sstclktype = CTL_SST_TS_TELEPHONE; - - /* - * Initialize modem and kill DTR. We skedaddle if this comes - * bum. - */ - if (!acts_write(peer, MODEM_SETUP)) { - (void) close(fd); - free(up); - return (0); - } - - /* - * Set up the driver timeout - */ - peer->nextdate = current_time + WAIT; + peer->flags &= ~FLAG_FIXPOLL; + up->bufptr = pp->a_lastcode; return (1); } @@ -471,16 +280,18 @@ acts_start ( */ static void acts_shutdown ( - int unit, + int unit, struct peer *peer ) { - register struct actsunit *up; + struct actsunit *up; struct refclockproc *pp; + /* + * Warning: do this only when a call is not in progress. + */ pp = peer->procptr; up = (struct actsunit *)pp->unitptr; - io_closeclock(&pp->io); free(up); } @@ -493,254 +304,326 @@ acts_receive ( struct recvbuf *rbufp ) { - register struct actsunit *up; + struct actsunit *up; struct refclockproc *pp; struct peer *peer; - char str[SMAX]; - int i; - char hangup = '%'; /* ACTS hangup */ - int day; /* day of the month */ - int month; /* month of the year */ - u_long mjd; /* Modified Julian Day */ - double dut1; /* DUT adjustment */ - double msADV; /* ACTS transmit advance (ms) */ - char flag; /* calibration flag */ -#ifndef CLOCK_PTBACTS - char utc[10]; /* this is NIST and you're not */ - u_int dst; /* daylight/standard time indicator */ - u_int leap; /* leap-second indicator */ -#else - char leapdir; /* leap direction */ - u_int leapmonth; /* month of leap */ -#endif + char tbuf[BMAX]; + char *tptr; + /* - * Initialize pointers and read the timecode and timestamp. If - * the OK modem status code, leave it where folks can find it. + * Initialize pointers and read the timecode and timestamp. Note + * we are in raw mode and victim of whatever the terminal + * interface kicks up; so, we have to reassemble messages from + * arbitrary fragments. Capture the timecode at the beginning of + * the message and at the '*' and '#' on-time characters. */ peer = (struct peer *)rbufp->recv_srcclock; pp = peer->procptr; up = (struct actsunit *)pp->unitptr; - pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, - &pp->lastrec); - if (pp->lencode == 0) { - if (strcmp(pp->a_lastcode, "OK") == 0) - pp->lencode = 2; - return; + pp->lencode = refclock_gtraw(rbufp, tbuf, BMAX - (up->bufptr - + pp->a_lastcode), &pp->lastrec); + for (tptr = tbuf; *tptr != '\0'; tptr++) { + if (*tptr == LF) { + if (up->bufptr == pp->a_lastcode) { + up->tstamp = pp->lastrec; + continue; + + } else { + *up->bufptr = '\0'; + acts_message(peer); + up->bufptr = pp->a_lastcode; + } + } else if (!iscntrl(*tptr)) { + *up->bufptr++ = *tptr; + if (*tptr == '*' || *tptr == '#') { + up->tstamp = pp->lastrec; + write(pp->io.fd, tptr, 1); + } + } } +} + + +/* + * acts_message - process message + */ +void +acts_message( + struct peer *peer + ) +{ + struct actsunit *up; + struct refclockproc *pp; + int dtr = TIOCM_DTR; + char tbuf[SMAX]; #ifdef DEBUG - if (debug) - printf("acts: state %d timecode %d %*s\n", up->state, - pp->lencode, pp->lencode, pp->a_lastcode); + u_int modem; #endif - switch (up->state) { - - case 0: + /* + * What to do depends on the state and the first token in the + * message. A NO token sends the message to the clockstats. + */ + pp = peer->procptr; + up = (struct actsunit *)pp->unitptr; +#ifdef DEBUG + ioctl(pp->io.fd, TIOCMGET, (char *)&modem); + sprintf(tbuf, "acts: %04x (%d %d) %lu %s", modem, up->state, + up->timer, strlen(pp->a_lastcode), pp->a_lastcode); + if (debug) + printf("%s\n", tbuf); +#endif + strncpy(tbuf, pp->a_lastcode, SMAX); + strtok(tbuf, " "); + if (strcmp(tbuf, "NO") == 0) + record_clock_stats(&peer->srcadr, pp->a_lastcode); + switch(up->state) { - /* - * State 0. We are not expecting anything. Probably - * modem disconnect noise. Go back to sleep. - */ + /* + * We are waiting for the OK response to the modem setup + * command. When this happens, raise DTR and dial the number + * followed by \r. + */ + case S_OK: + if (strcmp(tbuf, "OK") != 0) { + msyslog(LOG_ERR, "acts: setup error %s", + pp->a_lastcode); + acts_disc(peer); + return; + } + ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr); + up->state = S_DTR; + up->timer = DTR; return; - case 1: - - /* - * State 1. We are waiting for the call to be answered. - * All we care about here is CONNECT as the first token - * in the string. If the modem signals BUSY, ERROR, NO - * ANSWER, NO CARRIER or NO DIALTONE, we immediately - * hang up the phone. If CONNECT doesn't happen after - * ANSWER seconds, hang up the phone. If everything is - * okay, start the connect timeout and slide into state - * 2. - */ - if( strcmp(pp->a_lastcode, " ") == 0) { + /* + * We are waiting for the call to be answered. All we care about + * here is token CONNECT. Send the message to the clockstats. + */ + case S_CONNECT: + record_clock_stats(&peer->srcadr, pp->a_lastcode); + if (strcmp(tbuf, "CONNECT") != 0) { acts_disc(peer); return; } - if( strcmp(sys_phone[0],"DIRECT") != 0 ) { - (void)strncpy(str, strtok(pp->a_lastcode, " "), SMAX); - if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") == - 0 || strcmp(str, "NO") == 0) { - NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ - msyslog(LOG_NOTICE, - "clock %s ACTS modem status %s", - ntoa(&peer->srcadr), pp->a_lastcode); - acts_disc(peer); - } else if (strcmp(str, "CONNECT") == 0) { - peer->nextdate = current_time + CONNECT; - up->msgcnt = 0; - up->state++; - } - } else { - (void) strncpy(str,"CONNECT",7); - peer->nextdate = current_time + CONNECT; - up->msgcnt = 0; - up->state++; - } + up->state = S_FIRST; + up->timer = CONNECT; return; - case 2: - - /* - * State 2. The call has been answered and we are - * waiting for the first ACTS message. If this doesn't - * happen within the timecode timeout, hang up the - * phone. We probably got a wrong number or ACTS is - * down. - */ - peer->nextdate = current_time + TIMECODE; - up->state++; - } - /* - * Real yucky things here. Ignore everything except timecode - * messages, as determined by the message length. We told the - * terminal routines to end the line with '*' and the line - * discipline to strike a timestamp on that character. However, - * when the ACTS echo-delay scheme works, the '*' eventually - * becomes a '#'. In this case the message is ended by the <CR> - * that comes about 200 ms after the '#' and the '#' cannot be - * echoed at the proper time. But, this may not be a lose, since - * we already have good data from prior messages and only need - * the millisecond advance calculated by ACTS. So, if the - * message is long enough and has an on-time character at the - * right place, we consider the message (but not neccesarily the - * timestmap) to be valid. + * We are waiting for a timecode. Pass it to the parser. */ - if (pp->lencode != LENCODE) - return; + case S_FIRST: + case S_MSG: + acts_timecode(peer, pp->a_lastcode); + break; + } +} + +/* + * acts_timecode - identify the service and parse the timecode message + */ +void +acts_timecode( + struct peer *peer, /* peer structure pointer */ + char *str /* timecode string */ + ) +{ + struct actsunit *up; + struct refclockproc *pp; + int day; /* day of the month */ + int month; /* month of the year */ + u_long mjd; /* Modified Julian Day */ + double dut1; /* DUT adjustment */ + + u_int dst; /* ACTS daylight/standard time */ + u_int leap; /* ACTS leap indicator */ + double msADV; /* ACTS transmit advance (ms) */ + char utc[10]; /* ACTS timescale */ + char flag; /* ACTS on-time character (* or #) */ + + char synchar; /* WWVB synchronized indicator */ + char qualchar; /* WWVB quality indicator */ + char leapchar; /* WWVB leap indicator */ + char dstchar; /* WWVB daylight/savings indicator */ + int tz; /* WWVB timezone */ + + u_int leapmonth; /* PTB/NPL month of leap */ + char leapdir; /* PTB/NPL leap direction */ -#ifndef CLOCK_PTBACTS /* - * We apparently have a valid timecode message, so dismember it - * with sscan(). This routine does a good job in spotting syntax - * errors without becoming overly pedantic. - * - * D L D - * MJD YR MO DA H M S ST S UT1 msADV OTM - * 47222 88-03-02 21:39:15 83 0 +.3 045.0 UTC(NBS) * + * The parser selects the modem format based on the message + * length. Since the data are checked carefully, occasional + * errors due noise are forgivable. */ - if (sscanf(pp->a_lastcode, - "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %s %c", - &mjd, &pp->year, &month, &day, &pp->hour, &pp->minute, - &pp->second, &dst, &leap, &dut1, &msADV, utc, &flag) != 13) { - refclock_report(peer, CEVNT_BADREPLY); - return; - } -#else + pp = peer->procptr; + up = (struct actsunit *)pp->unitptr; + pp->nsec = 0; + switch(strlen(str)) { + /* - * Data format - * 0000000000111111111122222222223333333333444444444455555555556666666666777777777 7 - * 0123456789012345678901234567890123456789012345678901234567890123456789012345678 9 - * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500 * + * For USNO format on-time character '*', which is on a line by + * itself. Be sure a timecode has been received. */ - if (sscanf(pp->a_lastcode, - "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c", - &pp->second, &pp->year, &month, &day, &pp->hour, &pp->minute, &mjd, &dut1, &leapdir, &leapmonth, &msADV, &flag) != 12) { - refclock_report(peer, CEVNT_BADREPLY); + case 1: + if (*str == '*' && up->msgcnt > 0) + break; + return; - } -#endif + /* - * Some modems can't be trusted (the Practical Peripherals - * 9600SA comes to mind) and, even if they manage to unstick - * ACTS, the millisecond advance is wrong, so we use CLK_FLAG2 - * to disable echoes, if neccessary. + * ACTS format: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa + * UTC(NIST) *" */ - if ((flag == '*' || flag == '#') && !(pp->sloppyclockflag & - CLK_FLAG2)) - (void)write(pp->io.fd, &flag, 1); + case LENACTS: + if (sscanf(str, + "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c", + &mjd, &pp->year, &month, &day, &pp->hour, + &pp->minute, &pp->second, &dst, &leap, &dut1, + &msADV, utc, &flag) != 13) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + + /* + * Wait until ACTS has calculated the roundtrip delay. + * We don't need to do anything, as ACTS adjusts the + * on-time epoch. + */ + if (flag != '#') + return; + + pp->day = ymd2yd(pp->year, month, day); + pp->leap = LEAP_NOWARNING; + if (leap == 1) + pp->leap = LEAP_ADDSECOND; + else if (pp->leap == 2) + pp->leap = LEAP_DELSECOND; + memcpy(&pp->refid, REFACTS, 4); + if (up->msgcnt == 0) + record_clock_stats(&peer->srcadr, str); + up->msgcnt++; + break; /* - * The ACTS timecode format croaks in 2000. Life is short. - * Would only the timecode mavens resist the urge to express months - * of the year and days of the month in favor of days of the year. + * USNO format: "jjjjj nnn hhmmss UTC" */ - if (month < 1 || month > 12 || day < 1) { - refclock_report(peer, CEVNT_BADTIME); + case LENUSNO: + if (sscanf(str, "%5ld %3d %2d%2d%2d %3s", + &mjd, &pp->day, &pp->hour, &pp->minute, + &pp->second, utc) != 6) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + + /* + * Wait for the on-time character, which follows in a + * separate message. There is no provision for leap + * warning. + */ + pp->leap = LEAP_NOWARNING; + memcpy(&pp->refid, REFUSNO, 4); + if (up->msgcnt == 0) + record_clock_stats(&peer->srcadr, str); + up->msgcnt++; return; - } /* - * Depending on the driver, at this point we have a two-digit year - * or a four-digit year. Make sure we have a four-digit year. + * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ" */ - if ( pp->year < YEAR_PIVOT ) pp->year += 100; /* Y2KFixes */ - if ( pp->year < YEAR_BREAK ) pp->year += 1900; /* Y2KFixes */ - if ( !isleap_4(pp->year) ) { /* Y2KFixes */ - if (day > day1tab[month - 1]) { - refclock_report(peer, CEVNT_BADTIME); + case LENPTB: + if (sscanf(str, + "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c", + &pp->second, &pp->year, &month, &day, &pp->hour, + &pp->minute, &mjd, &dut1, &leapdir, &leapmonth, + &msADV, &flag) != 12) { + refclock_report(peer, CEVNT_BADREPLY); return; } - for (i = 0; i < month - 1; i++) - day += day1tab[i]; - } else { - if (day > day2tab[month - 1]) { - refclock_report(peer, CEVNT_BADTIME); - return; + pp->leap = LEAP_NOWARNING; + if (leapmonth == month) { + if (leapdir == '+') + pp->leap = LEAP_ADDSECOND; + else if (leapdir == '-') + pp->leap = LEAP_DELSECOND; } - for (i = 0; i < month - 1; i++) - day += day2tab[i]; - } - pp->day = day; + pp->day = ymd2yd(pp->year, month, day); + memcpy(&pp->refid, REFPTB, 4); + if (up->msgcnt == 0) + record_clock_stats(&peer->srcadr, str); + up->msgcnt++; + break; -#ifndef CLOCK_PTBACTS - if (leap == 1) - pp->leap = LEAP_ADDSECOND; - else if (pp->leap == 2) - pp->leap = LEAP_DELSECOND; -#else - if (leapmonth == month) { - if (leapdir == '+') - pp->leap = LEAP_ADDSECOND; - else if (leapdir == '-') - pp->leap = LEAP_DELSECOND; - } -#endif /* - * Colossal hack here. We process each sample in a trimmed-mean - * filter and determine the reference clock offset and - * dispersion. The fudge time1 value is added to each sample as - * received. If we collect MSGCNT samples before the '#' on-time - * character, we use the results of the filter as is. If the '#' - * is found before that, the adjusted msADV is used to correct - * the propagation delay. + * WWVB format 0: "I ddd hh:mm:ss DTZ=nn" + */ + case LENWWVB0: + if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d", + &synchar, &pp->day, &pp->hour, &pp->minute, + &pp->second, &dstchar, &tz) != 7) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + pp->leap = LEAP_NOWARNING; + if (synchar != ' ') + pp->leap = LEAP_NOTINSYNC; + memcpy(&pp->refid, REFWWVB, 4); + if (up->msgcnt == 0) + record_clock_stats(&peer->srcadr, str); + up->msgcnt++; + break; + + /* + * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD" */ - up->msgcnt++; - if (flag == '#') { - pp->offset += (msADV - up->msADV) * 1000 * 1e-6; - } else { - up->msADV = msADV; - if (!refclock_process(pp)) { - refclock_report(peer, CEVNT_BADTIME); + case LENWWVB2: + if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c", + &synchar, &qualchar, &pp->year, &pp->day, + &pp->hour, &pp->minute, &pp->second, &pp->nsec, + &dstchar, &leapchar, &dstchar) != 11) { + refclock_report(peer, CEVNT_BADREPLY); return; - } else if (up->msgcnt < MSGCNT) - return; + } + pp->nsec *= 1000000; + pp->leap = LEAP_NOWARNING; + if (synchar != ' ') + pp->leap = LEAP_NOTINSYNC; + else if (leapchar == 'L') + pp->leap = LEAP_ADDSECOND; + memcpy(&pp->refid, REFWWVB, 4); + if (up->msgcnt == 0) + record_clock_stats(&peer->srcadr, str); + up->msgcnt++; + break; + + /* + * None of the above. Just forget about it and wait for the next + * message or timeout. + */ + default: + return; } /* - * We have a filtered sample offset ready for peer processing. - * We use lastrec as both the reference time and receive time in - * order to avoid being cute, like setting the reference time - * later than the receive time, which may cause a paranoid - * protocol module to chuck out the data. Finaly, we unhook the - * timeout, arm for the next call, fold the tent and go home. - * The little dance with the '%' character is an undocumented - * ACTS feature that hangs up the phone real quick without - * waiting for carrier loss or long-space disconnect, but we do - * these clumsy things anyway. + * We have a valid timecode. The fudge time1 value is added to + * each sample by the main line routines. Note that in current + * telephone networks the propatation time can be different for + * each call and can reach 200 ms for some calls. */ + peer->refid = pp->refid; + pp->lastrec = up->tstamp; + if (!refclock_process(pp)) { + refclock_report(peer, CEVNT_BADTIME); + return; + } pp->lastref = pp->lastrec; - refclock_receive(peer); - record_clock_stats(&peer->srcadr, pp->a_lastcode); - pp->sloppyclockflag &= ~CLK_FLAG1; - up->pollcnt = 0; - (void)write(pp->io.fd, &hangup, 1); - up->state = 0; - acts_disc(peer); + if (peer->disp > MAXDISTANCE) + refclock_receive(peer); + if (up->state != S_MSG) { + up->state = S_MSG; + up->timer = TIMECODE; + } } @@ -749,234 +632,298 @@ acts_receive ( */ static void acts_poll ( - int unit, + int unit, struct peer *peer ) { - register struct actsunit *up; + struct actsunit *up; struct refclockproc *pp; /* - * If the driver is running, we set the enable flag (fudge - * flag1), which causes the driver timeout routine to initiate a - * call to ACTS. If not, the enable flag can be set using - * ntpdc. If this is the sustem peer, then follow the system - * poll interval. + * This routine is called at every system poll. All it does is + * set flag1 under certain conditions. The real work is done by + * the timeout routine and state machine. */ pp = peer->procptr; up = (struct actsunit *)pp->unitptr; + switch (peer->ttl) { - if (up->run) { + /* + * In manual mode the calling program is activated by the ntpdc + * program using the enable flag (fudge flag1), either manually + * or by a cron job. + */ + case MODE_MANUAL: + /* fall through */ + break; + + /* + * In automatic mode the calling program runs continuously at + * intervals determined by the poll event or specified timeout. + */ + case MODE_AUTO: pp->sloppyclockflag |= CLK_FLAG1; - if (peer == sys_peer) - peer->hpoll = sys_poll; - else - peer->hpoll = peer->minpoll; + break; + + /* + * In backup mode the calling program runs continuously as long + * as either no peers are available or this peer is selected. + */ + case MODE_BACKUP: + if (sys_peer == NULL || sys_peer == peer) + pp->sloppyclockflag |= CLK_FLAG1; + break; } - acts_timeout (peer); - return; } /* - * acts_timeout - called by the timer interrupt + * acts_timer - called at one-second intervals */ static void -acts_timeout ( +acts_timer( + int unit, struct peer *peer ) { - register struct actsunit *up; + struct actsunit *up; struct refclockproc *pp; - int dtr = TIOCM_DTR; /* - * If a timeout occurs in other than state 0, the call has - * failed. If in state 0, we just see if there is other work to - * do. + * This routine implments a timeout which runs for a programmed + * interval. The counter is initialized by the state machine and + * counts down to zero. Upon reaching zero, the state machine is + * called. If flag1 is set while in S_IDLE state, force a + * timeout. */ pp = peer->procptr; up = (struct actsunit *)pp->unitptr; - if (up->state) { - acts_disc(peer); + if (pp->sloppyclockflag & CLK_FLAG1 && up->state == S_IDLE) { + acts_timeout(peer); return; } - switch (peer->ttl) { + if (up->timer == 0) + return; + + up->timer--; + if (up->timer == 0) + acts_timeout(peer); +} + + +/* + * acts_timeout - called on timeout + */ +static void +acts_timeout( + struct peer *peer + ) +{ + struct actsunit *up; + struct refclockproc *pp; + int fd; + char device[20]; + char lockfile[128], pidbuf[8]; + char tbuf[BMAX]; + + /* + * The state machine is driven by messages from the modem, when + * first stated and at timeout. + */ + pp = peer->procptr; + up = (struct actsunit *)pp->unitptr; + pp->sloppyclockflag &= ~CLK_FLAG1; + if (sys_phone[up->retry] == NULL && !(pp->sloppyclockflag & + CLK_FLAG3)) { + msyslog(LOG_ERR, "acts: no phones"); + return; + } + switch(up->state) { + + /* + * System poll event. Lock the modem port and open the device. + */ + case S_IDLE: /* - * In manual mode the ACTS calling program is activated - * by the ntpdc program using the enable flag (fudge - * flag1), either manually or by a cron job. + * Lock the modem port. If busy, retry later. Note: if + * something fails between here and the close, the lock + * file may not be removed. */ - case MODE_MANUAL: - up->run = 0; - break; + if (pp->sloppyclockflag & CLK_FLAG2) { + sprintf(lockfile, LOCKFILE, up->unit); + fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, + 0644); + if (fd < 0) { + msyslog(LOG_ERR, "acts: port busy"); + return; + } + sprintf(pidbuf, "%d\n", (u_int)getpid()); + write(fd, pidbuf, strlen(pidbuf)); + close(fd); + } /* - * In automatic mode the ACTS calling program runs - * continuously at intervals determined by the sys_poll - * variable. + * Open the device in raw mode and link the I/O. */ - case MODE_AUTO: - if (!up->run) - pp->sloppyclockflag |= CLK_FLAG1; - up->run = 1; - break; + if (!pp->io.fd) { + sprintf(device, DEVICE, up->unit); + fd = refclock_open(device, SPEED232, + LDISC_ACTS | LDISC_RAW | LDISC_REMOTE); + if (fd == 0) { + return; + } + pp->io.fd = fd; + if (!io_addclock(&pp->io)) { + msyslog(LOG_ERR, + "acts: addclock fails"); + close(fd); + pp->io.fd = 0; + return; + } + } /* - * In backup mode the ACTS calling program is disabled, - * unless no system peer has been selected for MAXOUTAGE - * (3600 s). Once enabled, it runs until some other NTP - * peer shows up. + * If the port is directly connected to the device, skip + * the modem business and send 'T' for Spectrabum. */ - case MODE_BACKUP: - if (!up->run && sys_peer == 0) { - if (current_time - last_time > MAXOUTAGE) { - up->run = 1; - peer->hpoll = peer->minpoll; - NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ - msyslog(LOG_NOTICE, - "clock %s ACTS backup started ", - ntoa(&peer->srcadr)); + if (pp->sloppyclockflag & CLK_FLAG3) { + if (write(pp->io.fd, "T", 1) < 0) { + msyslog(LOG_ERR, "acts: write %m"); + return; } - } else if (up->run && sys_peer->sstclktype != CTL_SST_TS_TELEPHONE) { - peer->hpoll = peer->minpoll; - up->run = 0; - NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ - msyslog(LOG_NOTICE, - "clock %s ACTS backup stopped", - ntoa(&peer->srcadr)); + up->state = S_FIRST; + up->timer = CONNECT; + return; } - break; - default: - msyslog(LOG_ERR, - "clock %s ACTS invalid mode", ntoa(&peer->srcadr)); - } + /* + * Initialize the modem. This works with Hayes commands. + */ +#ifdef DEBUG + if (debug) + printf("acts: setup %s\n", MODEM_SETUP); +#endif + if (write(pp->io.fd, MODEM_SETUP, strlen(MODEM_SETUP)) < + 0) { + msyslog(LOG_ERR, "acts: write %m"); + return; + } + up->state = S_OK; + up->timer = SETUP; + return; /* - * The fudge flag1 is used as an enable/disable; if set either - * by the code or via ntpdc, the ACTS calling program is - * started; if reset, the phones stop ringing. + * In OK state the modem did not respond to setup. */ - if (!(pp->sloppyclockflag & CLK_FLAG1)) { - up->pollcnt = 0; - peer->nextdate = current_time + IDLE; - return; - } + case S_OK: + msyslog(LOG_ERR, "acts: no modem"); + break; /* - * Initiate a call to the ACTS service. If we wind up here in - * other than state 0, a successful call could not be completed - * within minpoll seconds. We advance to the next modem dial - * string. If none are left, we log a notice and clear the - * enable flag. For future enhancement: call the site RP and - * leave an obscene message in his voicemail. + * In DTR state we are waiting for the modem to settle down + * before hammering it with a dial command. */ - if (sys_phone[up->pollcnt][0] == '\0') { - refclock_report(peer, CEVNT_TIMEOUT); - NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ - msyslog(LOG_NOTICE, - "clock %s ACTS calling program terminated", - ntoa(&peer->srcadr)); - pp->sloppyclockflag &= ~CLK_FLAG1; + case S_DTR: + sprintf(tbuf, "DIAL #%d %s", up->retry, + sys_phone[up->retry]); + record_clock_stats(&peer->srcadr, tbuf); #ifdef DEBUG if (debug) - printf("acts: calling program terminated\n"); + printf("%s\n", tbuf); #endif - up->pollcnt = 0; - peer->nextdate = current_time + IDLE; + write(pp->io.fd, sys_phone[up->retry], + strlen(sys_phone[up->retry])); + write(pp->io.fd, "\r", 1); + up->state = S_CONNECT; + up->timer = ANSWER; return; - } /* - * Raise DTR, call ACTS and start the answer timeout. We think - * it strange if the OK status has not been received from the - * modem, but plow ahead anyway. + * In CONNECT state the call did not complete. */ - if (strcmp(pp->a_lastcode, "OK") != 0) - NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ - msyslog(LOG_NOTICE, "clock %s ACTS no modem status", - ntoa(&peer->srcadr)); - (void)ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr); - (void)acts_write(peer, sys_phone[up->pollcnt]); - NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ - msyslog(LOG_NOTICE, "clock %s ACTS calling %s\n", - ntoa(&peer->srcadr), sys_phone[up->pollcnt]); - up->state = 1; - up->pollcnt++; - pp->polls++; - peer->nextdate = current_time + ANSWER; - return; -} - + case S_CONNECT: + msyslog(LOG_ERR, "acts: no answer"); + break; -/* - * acts_disc - disconnect the call and wait for the ruckus to cool - */ -static void -acts_disc ( - struct peer *peer - ) -{ - register struct actsunit *up; - struct refclockproc *pp; - int dtr = TIOCM_DTR; + /* + * In FIRST state no messages were received. + */ + case S_FIRST: + msyslog(LOG_ERR, "acts: no messages"); + break; /* - * We should never get here other than in state 0, unless a call - * has timed out. We drop DTR, which will reliably get the modem - * off the air, even while ACTS is hammering away full tilt. + * In CLOSE state hangup is complete. Close the doors and + * windows and get some air. */ - pp = peer->procptr; - up = (struct actsunit *)pp->unitptr; - (void)ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr); - if (up->state > 0) { - NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ - msyslog(LOG_NOTICE, "clock %s ACTS call failed %d", - ntoa(&peer->srcadr), up->state); -#ifdef DEBUG - if (debug) - printf("acts: call failed %d\n", up->state); -#endif - up->state = 0; + case S_CLOSE: + + /* + * Close the device and unlock a shared modem. + */ + if (pp->io.fd) { + io_closeclock(&pp->io); + close(pp->io.fd); + if (pp->sloppyclockflag & CLK_FLAG2) { + sprintf(lockfile, LOCKFILE, up->unit); + unlink(lockfile); + } + pp->io.fd = 0; + } + + /* + * If messages were received, fold the tent and wait for + * the next poll. If no messages and there are more + * numbers to dial, retry after a short wait. + */ + up->bufptr = pp->a_lastcode; + up->timer = 0; + up->state = S_IDLE; + if ( up->msgcnt == 0) { + up->retry++; + if (sys_phone[up->retry] == NULL) + up->retry = 0; + else + up->timer = SETUP; + } else { + up->retry = 0; + } + up->msgcnt = 0; + return; } - peer->nextdate = current_time + WAIT; + acts_disc(peer); } /* - * acts_write - write a message to the serial port + * acts_disc - disconnect the call and clean the place up. */ -static int -acts_write ( - struct peer *peer, - const char *str +static void +acts_disc ( + struct peer *peer ) { - register struct actsunit *up; + struct actsunit *up; struct refclockproc *pp; - int len; - int code; - char cr = '\r'; + int dtr = TIOCM_DTR; /* - * Not much to do here, other than send the message, handle - * debug and report faults. + * We get here if the call terminated successfully or if an + * error occured. If the median filter has something in it,feed + * the data to the clock filter. If a modem port, drop DTR to + * force command mode and send modem hangup. */ pp = peer->procptr; up = (struct actsunit *)pp->unitptr; - len = strlen(str); -#ifdef DEBUG - if (debug) - printf("acts: state %d send %d %s\n", up->state, len, - str); -#endif - code = write(pp->io.fd, str, (unsigned)len) == len; - code &= write(pp->io.fd, &cr, 1) == 1; - if (!code) - refclock_report(peer, CEVNT_FAULT); - return (code); + if (up->msgcnt > 0) + refclock_receive(peer); + if (!(pp->sloppyclockflag & CLK_FLAG3)) { + ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr); + write(pp->io.fd, MODEM_HANGUP, strlen(MODEM_HANGUP)); + } + up->timer = SETUP; + up->state = S_CLOSE; } #else |