From: Harlan Stenn Date: Fri, 31 Dec 2010 06:45:11 +0000 (-0500) Subject: refclock_acts.c updates from Dave Mills X-Git-Tag: NTP_4_2_7P107~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9f93d5743ba576b84c597959f640d5228d059c5a;p=thirdparty%2Fntp.git refclock_acts.c updates from Dave Mills bk: 4d1d7bf7KpJ15hfKd6C7fh5m7RzQKg --- diff --git a/ChangeLog b/ChangeLog index 915f7d4af..061faceb8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ * Move ntp_control.h variable IDs to ntp_control.c, remove their use by ntpq. They are implementation details private to ntpd. [Bug 597] was caused by ntpq's reliance on these IDs it need not know about. +* refclock_acts.c updates from Dave Mills. (4.2.7p106) 2010/12/30 Released by Harlan Stenn * from 4.2.6p3: Update genCommitLog for the bk-5 release. (4.2.7p105) 2010/12/29 Released by Harlan Stenn diff --git a/ntpd/refclock_acts.c b/ntpd/refclock_acts.c index 0429de60a..3a4d2f923 100644 --- a/ntpd/refclock_acts.c +++ b/ntpd/refclock_acts.c @@ -59,18 +59,17 @@ * * flag1 force a call in manual mode * flag2 enable port locking (not verified) - * flag3 no modem; port is directly connected to device + * flag3 not used * 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. + * Ordinarily, the serial port is connected to a modem and the phones + * list is defined. If no phones list is defined, the port can be + * connected directly to a device or another computer. In this case the + * driver will send a single character 'T' at each poll event. If + * fudge flag2 is enabled, port locking allows the modem to be shared + * when not in use by this driver. */ /* * National Institute of Science and Technology (NIST) @@ -131,22 +130,21 @@ #define DEVICE "/dev/acts%d" /* device name and unit */ #define SPEED232 B9600 /* uart speed (9600 baud) */ #define PRECISION (-10) /* precision assumed (about 1 ms) */ -#define LOCKFILE "/var/spool/locks/LCK..cua%d" +#define LOCKFILE "/var/spool/lock/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 */ #define MAXPHONE 10 /* max number of phone numbers */ /* - * Calling program modes + * Calling program modes (mode) */ -#define MODE_AUTO 0 /* automatic mode */ -#define MODE_BACKUP 1 /* backup mode */ +#define MODE_BACKUP 0 /* backup mode */ +#define MODE_AUTO 1 /* automatic mode */ #define MODE_MANUAL 2 /* manual mode */ /* - * Service identifiers. + * Service identifiers (message length) */ #define REFACTS "NIST" /* NIST reference ID */ #define LENACTS 50 /* NIST format */ @@ -167,33 +165,30 @@ * &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 + * L1 set modem speaker volume to low level * M1 speaker enabled until carrier detect * Q0 return result codes * V1 return result codes as English words + * Y1 enable long-space disconnect */ -#define MODEM_SETUP "ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */ -#define MODEM_HANGUP "ATH\r" /* modem disconnect */ +#define MODEM_SETUP "ATB1&C0&D2E0L1M1Q0V1Y1" /* modem setup */ /* * Timeouts (all in seconds) */ #define SETUP 3 /* setup timeout */ -#define DTR 1 /* DTR timeout */ +#define REDIAL 30 /* redial timeout */ #define ANSWER 60 /* answer timeout */ -#define CONNECT 20 /* first valid message timeout */ -#define TIMECODE 30 /* all valid messages timeout */ +#define TIMECODE 60 /* message timeout */ +#define MAXCODE 10 /* max timecodes */ /* * State machine codes */ #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 */ +#define S_SETUP 1 /* send modem setup */ +#define S_CONNECT 2 /* wait for answer */ +#define S_MSG 3 /* wait for timecode */ /* * Unit control structure @@ -217,9 +212,9 @@ static void acts_receive (struct recvbuf *); static void acts_message (struct peer *); static void acts_timecode (struct peer *, char *); static void acts_poll (int, struct peer *); -static void acts_timeout (struct peer *); -static void acts_disc (struct peer *); +static void acts_timeout (struct peer *, int); static void acts_timer (int, struct peer *); +static void acts_close (struct peer *); /* * Transfer vector (conditional structure name) @@ -332,7 +327,8 @@ acts_receive ( *up->bufptr++ = *tptr; if (*tptr == '*' || *tptr == '#') { up->tstamp = pp->lastrec; - write(pp->io.fd, tptr, 1); + if (write(pp->io.fd, tptr, 1) < 0) + msyslog(LOG_ERR, "acts: write echo fails %m"); } } } @@ -349,77 +345,342 @@ acts_message( { struct actsunit *up; struct refclockproc *pp; - int dtr = TIOCM_DTR; - char tbuf[SMAX]; -#ifdef DEBUG - u_int modem; -#endif + char tbuf[BMAX]; + int dtr = TIOCM_DTR; /* * What to do depends on the state and the first token in the - * message. */ + * message. + */ pp = peer->procptr; up = (struct actsunit *)pp->unitptr; + #ifdef DEBUG - ioctl(pp->io.fd, TIOCMGET, (char *)&modem); - snprintf(tbuf, sizeof(tbuf), "acts: %04x (%d %d) %lu %s", modem, - up->state, up->timer, (u_long)strlen(pp->a_lastcode), - pp->a_lastcode); if (debug) - printf("%s\n", tbuf); + printf("acts: %d %s\n", strlen(pp->a_lastcode), + pp->a_lastcode); #endif /* - * Extract the first token in the line. A NO token sends the - * message to the clockstats. + * Extract the first token in the line. */ - strncpy(tbuf, pp->a_lastcode, SMAX); + strncpy(tbuf, pp->a_lastcode, BMAX); strtok(tbuf, " "); - if (strcmp(tbuf, "NO") == 0) { + switch(up->state) { + + /* + * We are waiting for the OK response to the modem setup + * command. When this happens, dial the number followed. + * If anything other than OK is received, just ignore it + * and wait for timeoue. + */ + case S_SETUP: + if (strcmp(tbuf, "OK") != 0) + break; + + snprintf(tbuf, sizeof(tbuf), "DIAL #%d %s", up->retry, + sys_phone[up->retry]); + report_event(PEVNT_CLOCK, peer, tbuf); + ioctl(pp->io.fd, TIOCMBIS, &dtr); + if (write(pp->io.fd, sys_phone[up->retry], + strlen(sys_phone[up->retry])) < 0) + msyslog(LOG_ERR, "acts: write DIAL fails %m"); + write(pp->io.fd, "\r", 1); + up->retry++; + up->state = S_CONNECT; + up->timer = ANSWER; + return; + + /* + * We are waiting for the CONNECT response to the dial + * command. When this happens, listen for timecodes. If + * somthing other than CONNECT is received, like BUSY + * or NO CARRIER, abort the call. + */ + case S_CONNECT: + if (strcmp(tbuf, "CONNECT") != 0) + break; + report_event(PEVNT_CLOCK, peer, pp->a_lastcode); + up->state = S_MSG; + up->timer = TIMECODE; + return; + + /* + * We are waiting for a timecode response. Pass it to + * the parser. If NO CARRIER is received, save the + * messages and abort the call. + */ + case S_MSG: + if (strcmp(tbuf, "NO") == 0) + report_event(PEVNT_CLOCK, peer, pp->a_lastcode); + if (up->msgcnt < MAXCODE) + acts_timecode(peer, pp->a_lastcode); + else + acts_timeout(peer, S_MSG); return; } - switch(up->state) { /* - * We are waiting for the OK response to the modem setup - * command. When this happens, raise DTR and dial the number - * followed by \r. + * Other response. Tell us about it. */ - case S_OK: - if (strcmp(tbuf, "OK") != 0) { - msyslog(LOG_ERR, "acts: setup error %s", - pp->a_lastcode); - acts_disc(peer); + report_event(PEVNT_CLOCK, peer, pp->a_lastcode); + acts_close(peer); +} + + +/* + * acts_timeout - called on timeout + */ +static void +acts_timeout( + struct peer *peer, + int dstate + ) +{ + 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; + switch(dstate) { + + /* + * System poll event. Lock the modem port, open the device + * and send the setup command. + */ + case S_IDLE: + + /* + * Lock the modem port. If busy, retry later. Note: if + * something fails between here and the close, the lock + * file may not be removed. + */ + if (pp->sloppyclockflag & CLK_FLAG2) { + snprintf(lockfile, sizeof(lockfile), LOCKFILE, + up->unit); + fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, + 0644); + if (fd < 0) { + report_event(PEVNT_CLOCK, peer, "acts: port busy"); + return; + } + snprintf(pidbuf, sizeof(pidbuf), "%d\n", + (u_int)getpid()); + if (write(fd, pidbuf, strlen(pidbuf)) < 0) + msyslog(LOG_ERR, "acts: write lock fails %m"); + close(fd); + } + + /* + * Open the device in raw mode and link the I/O. + */ + snprintf(device, sizeof(device), DEVICE, + up->unit); + fd = refclock_open(device, SPEED232,LDISC_ACTS | + LDISC_RAW | LDISC_REMOTE); + if (fd < 0) { + msyslog(LOG_ERR, "acts: open fails %m"); return; } - ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr); - up->state = S_DTR; - up->timer = DTR; + pp->io.fd = fd; + if (!io_addclock(&pp->io)) { + msyslog(LOG_ERR, "acts: addclock fails"); + return; + } + tcflush(pp->io.fd, TIOCFLUSH); + up->msgcnt = 0; + up->bufptr = pp->a_lastcode; + + /* + * If the port is directly connected to the device, skip + * the modem business and send 'T' for Spectrabum. + */ + if (sys_phone[up->retry] == NULL) { + if (write(pp->io.fd, "T", 1) < 0) + msyslog(LOG_ERR, "acts: write T fails %m"); + up->state = S_MSG; + up->timer = TIMECODE; + return; + } + + /* + * Initialize the modem. This works with Hayes comapatible + * modems. + */ + snprintf(tbuf, sizeof(tbuf), "SETUP %s", MODEM_SETUP); + report_event(PEVNT_CLOCK, peer, tbuf); + if (write(pp->io.fd, MODEM_SETUP, strlen(MODEM_SETUP)) < 0) + msyslog(LOG_ERR, "acts: write SETUP fails %m"); + write(pp->io.fd, "\r", 1); + up->state = S_SETUP; + up->timer = SETUP; return; /* - * We are waiting for the call to be answered. All we care about - * here is token CONNECT. Send the message to the clockstats. + * In SETUP state the modem did not respond to setup. + * the call. + */ + case S_SETUP: + report_event(PEVNT_CLOCK, peer, "no modem"); + break; + + /* + * In CONNECT state the call did not complete. Abort + * the call. */ case S_CONNECT: - report_event(PEVNT_CLOCK, peer, pp->a_lastcode); - if (strcmp(tbuf, "CONNECT") != 0) { - acts_disc(peer); + report_event(PEVNT_CLOCK, peer, "no answer"); + break; + + /* + * In MSG states no further timecoees are expected. If any + * timecodes have arrived, update the clock. In any case, + * ternuabte the cakk. + */ + case S_MSG: + if (up->msgcnt == 0) + report_event(PEVNT_CLOCK, peer, "no timecodes"); + else + refclock_receive(peer); + break; + } + acts_close(peer); +} + + +/* + * acts_close - close and prepare for next call. + * + * In ClOSE state no further protocol actions are required + * other than to close and release the device and prepare to + * dial the next number if necessary. + */ +void +acts_close( + struct peer *peer + ) +{ + struct actsunit *up; + struct refclockproc *pp; + char lockfile[128]; + int dtr = TIOCM_DTR; + + pp = peer->procptr; + up = (struct actsunit *)pp->unitptr; + if (pp->io.fd != 0) { + report_event(PEVNT_CLOCK, peer, "close"); + ioctl(pp->io.fd, TIOCMBIC, &dtr); + tcflush(pp->io.fd, TIOCFLUSH); + io_closeclock(&pp->io); + pp->io.fd = 0; + } + if (pp->sloppyclockflag & CLK_FLAG2) { + snprintf(lockfile, sizeof(lockfile), + LOCKFILE, up->unit); + unlink(lockfile); + } + if (up->msgcnt == 0 && up->retry > 0) { + if (sys_phone[up->retry] != NULL) { + up->state = S_IDLE; + up->timer = REDIAL; return; } - up->state = S_FIRST; - up->timer = CONNECT; + } + up->state = S_IDLE; + up->timer = 0; +} + + +/* + * acts_poll - called by the transmit routine + */ +static void +acts_poll ( + int unit, + struct peer *peer + ) +{ + struct actsunit *up; + struct refclockproc *pp; + + /* + * 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) { + + /* + * 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: return; /* - * We are waiting for a timecode. Pass it to the parser. + * In automatic mode the calling program runs continuously at + * intervals determined by the poll event or specified timeout. */ - case S_FIRST: - case S_MSG: - acts_timecode(peer, pp->a_lastcode); + case MODE_AUTO: + 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)) + return; + break; } + up->retry = 0; + acts_timeout(peer, S_IDLE); +} + + +/* + * acts_timer - called at one-second intervals + */ +static void +acts_timer( + int unit, + struct peer *peer + ) +{ + struct actsunit *up; + struct refclockproc *pp; + + /* + * 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 timer is zero, force a call. + */ + pp = peer->procptr; + up = (struct actsunit *)pp->unitptr; + if (up->timer == 0) { + if (pp->sloppyclockflag & CLK_FLAG1) { + pp->sloppyclockflag &= ~CLK_FLAG1; + acts_timeout(peer, S_IDLE); + } + } else { + up->timer--; + if (up->timer == 0) + acts_timeout(peer, up->state); + } } /* @@ -492,9 +753,10 @@ acts_timecode( * We don't need to do anything, as ACTS adjusts the * on-time epoch. */ - if (flag != '#') +/* +if (flag != '#') return; - +*/ pp->day = ymd2yd(pp->year, month, day); pp->leap = LEAP_NOWARNING; if (leap == 1) @@ -528,7 +790,7 @@ acts_timecode( if (up->msgcnt == 0) record_clock_stats(&peer->srcadr, str); up->msgcnt++; - return; + break; /* * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ" @@ -615,321 +877,14 @@ acts_timecode( */ peer->refid = pp->refid; pp->lastrec = up->tstamp; + if (up->msgcnt == 0) + return; + if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); return; } pp->lastref = pp->lastrec; - if (up->state != S_MSG) { - up->state = S_MSG; - up->timer = TIMECODE; - } -} - - -/* - * acts_poll - called by the transmit routine - */ -static void -acts_poll ( - int unit, - struct peer *peer - ) -{ - struct actsunit *up; - struct refclockproc *pp; - - /* - * 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) { - - /* - * 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; - 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_timer - called at one-second intervals - */ -static void -acts_timer( - int unit, - struct peer *peer - ) -{ - struct actsunit *up; - struct refclockproc *pp; - - /* - * 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 (pp->sloppyclockflag & CLK_FLAG1 && up->state == S_IDLE) { - acts_timeout(peer); - return; - } - 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[SMAX]; - - /* - * 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: - - /* - * Lock the modem port. If busy, retry later. Note: if - * something fails between here and the close, the lock - * file may not be removed. - */ - if (pp->sloppyclockflag & CLK_FLAG2) { - snprintf(lockfile, sizeof(lockfile), LOCKFILE, - up->unit); - fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, - 0644); - if (fd < 0) { - msyslog(LOG_ERR, "acts: port busy"); - return; - } - snprintf(pidbuf, sizeof(pidbuf), "%d\n", - (u_int)getpid()); - write(fd, pidbuf, strlen(pidbuf)); - close(fd); - } - - /* - * Open the device in raw mode and link the I/O. - */ - if (!pp->io.fd) { - snprintf(device, sizeof(device), DEVICE, - up->unit); - fd = refclock_open(device, SPEED232, - LDISC_ACTS | LDISC_RAW | LDISC_REMOTE); - if (fd == 0) { - msyslog(LOG_ERR, - "acts: open fails"); - return; - } - pp->io.fd = fd; - if (!io_addclock(&pp->io)) { - msyslog(LOG_ERR, - "acts: addclock fails"); - close(fd); - pp->io.fd = 0; - return; - } - } - - /* - * If the port is directly connected to the device, skip - * the modem business and send 'T' for Spectrabum. - */ - if (pp->sloppyclockflag & CLK_FLAG3) { - if (write(pp->io.fd, "T", 1) < 0) { - msyslog(LOG_ERR, "acts: write %m"); - return; - } - up->state = S_FIRST; - up->timer = CONNECT; - return; - } - - /* - * 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; - - /* - * In OK state the modem did not respond to setup. - */ - case S_OK: - msyslog(LOG_ERR, "acts: no modem"); - break; - - /* - * In DTR state we are waiting for the modem to settle down - * before hammering it with a dial command. - */ - case S_DTR: - snprintf(tbuf, sizeof(tbuf), "DIAL #%d %s", up->retry, - sys_phone[up->retry]); - report_event(PEVNT_CLOCK, peer, tbuf); -#ifdef DEBUG - if (debug) - printf("%s\n", tbuf); -#endif - 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; - - /* - * In CONNECT state the call did not complete. - */ - case S_CONNECT: - msyslog(LOG_ERR, "acts: no answer"); - break; - - /* - * In FIRST state no messages were received. - */ - case S_FIRST: - msyslog(LOG_ERR, "acts: no messages"); - break; - - /* - * In CLOSE state hangup is complete. Close the doors and - * windows and get some air. - */ - 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) { - snprintf(lockfile, sizeof(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; - } - acts_disc(peer); -} - - -/* - * acts_disc - disconnect the call and clean the place up. - */ -static void -acts_disc ( - struct peer *peer - ) -{ - struct actsunit *up; - struct refclockproc *pp; - int dtr = TIOCM_DTR; - - /* - * 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; - 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 int refclock_acts_bs;