#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#if defined(REFCLOCK) && defined(CLOCK_USNO)
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_unixtime.h"
#include "ntp_refclock.h"
#include "ntp_stdlib.h"
#include "ntp_control.h"
#include <stdio.h>
#include <ctype.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#define DEVICE "/dev/cua%d"
#define LOCKFILE "/var/lock/LCK..cua%d"
#define PHONE "atdt 202 653 0351"
#define SPEED232 B1200
#define PRECISION (-10)
#define REFID "USNO"
#define DESCRIPTION "Naval Observatory dialup"
#define MODE_AUTO 0
#define MODE_BACKUP 1
#define MODE_MANUAL 2
#define MSGCNT 10
#define SMAX 80
#define LENCODE 20
#define USNO_MINPOLL 10
#define USNO_MAXPOLL 14
#define MAXOUTAGE 3600
#define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1"
#define MODEM_HANGUP "ATH"
#define IDLE 60
#define WAIT 2
#define ANSWER 30
#define CONNECT 10
#define TIMECODE (MSGCNT+16)
struct usnounit {
int pollcnt;
int state;
int run;
int msgcnt;
long redial;
int unit;
};
static int usno_start P((int, struct peer *));
static void usno_shutdown P((int, struct peer *));
static void usno_poll P((int, struct peer *));
static void usno_disc P((struct peer *));
#if 0
static void usno_timeout P((struct peer *));
static void usno_receive P((struct recvbuf *));
static int usno_write P((struct peer *, const char *));
#endif
struct refclock refclock_usno = {
usno_start,
usno_shutdown,
usno_poll,
noentry,
noentry,
noentry,
NOFLAGS
};
static int
usno_start(
int unit,
struct peer *peer
)
{
register struct usnounit *up;
struct refclockproc *pp;
pp = peer->procptr;
peer->precision = PRECISION;
pp->clockdesc = DESCRIPTION;
memcpy((char *)&pp->refid, REFID, 4);
peer->minpoll = USNO_MINPOLL;
peer->maxpoll = USNO_MAXPOLL;
peer->sstclktype = CTL_SST_TS_TELEPHONE;
if (!(up = (struct usnounit *)
emalloc(sizeof(struct usnounit))))
return (0);
memset((char *)up, 0, sizeof(struct usnounit));
up->unit = unit;
pp->unitptr = (caddr_t)up;
peer->nextdate = current_time + WAIT;
return (1);
}
static void
usno_shutdown(
int unit,
struct peer *peer
)
{
register struct usnounit *up;
struct refclockproc *pp;
#ifdef DEBUG
if (debug)
printf("usno: clock %s shutting down\n", ntoa(&peer->srcadr));
#endif
pp = peer->procptr;
up = (struct usnounit *)pp->unitptr;
usno_disc(peer);
free(up);
}
#if 0
static void
usno_receive(
struct recvbuf *rbufp
)
{
register struct usnounit *up;
struct refclockproc *pp;
struct peer *peer;
char str[SMAX];
u_long mjd;
static int day, hour, minute, second;
peer = (struct peer *)rbufp->recv_srcclock;
pp = peer->procptr;
up = (struct usnounit *)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;
}
#ifdef DEBUG
if (debug)
printf("usno: timecode %d %s\n", pp->lencode,
pp->a_lastcode);
#endif
switch (up->state) {
case 0:
return;
case 1:
return;
case 2:
(void)strncpy(str, strtok(pp->a_lastcode, " "), SMAX);
if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") ==
0 || strcmp(str, "NO") == 0) {
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_NOTICE,
"clock %s USNO modem status %s",
ntoa(&peer->srcadr), pp->a_lastcode);
usno_disc(peer);
} else if (strcmp(str, "CONNECT") == 0) {
peer->nextdate = current_time + CONNECT;
up->msgcnt = 0;
up->state++;
} else {
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_WARNING,
"clock %s USNO unknown modem status %s",
ntoa(&peer->srcadr), pp->a_lastcode);
}
return;
case 3:
peer->nextdate = current_time + TIMECODE;
up->state++;
return;
case 4:
if (pp->lencode == LENCODE) {
if (sscanf(pp->a_lastcode, "%5ld %3d %2d%2d%2d UTC",
&mjd, &day, &hour, &minute, &second) != 5) {
#ifdef DEBUG
if (debug)
printf("usno: bad timecode format\n");
#endif
refclock_report(peer, CEVNT_BADREPLY);
} else
up->msgcnt++;
return;
} else if (pp->lencode != 1 || !up->msgcnt)
return;
}
pp->leap = LEAP_NOWARNING;
pp->day = day;
pp->hour = hour;
pp->minute = minute;
pp->second = second;
if (!refclock_process(pp)) {
#ifdef DEBUG
if (debug)
printf("usno: time rejected\n");
#endif
refclock_report(peer, CEVNT_BADTIME);
return;
} else if (up->msgcnt < MSGCNT)
return;
record_clock_stats(&peer->srcadr, pp->a_lastcode);
refclock_receive(peer);
pp->sloppyclockflag &= ~CLK_FLAG1;
up->pollcnt = 0;
up->state = 0;
usno_disc(peer);
}
#endif
static void
usno_poll(
int unit,
struct peer *peer
)
{
register struct usnounit *up;
struct refclockproc *pp;
pp = peer->procptr;
up = (struct usnounit *)pp->unitptr;
if (up->run) {
pp->sloppyclockflag |= CLK_FLAG1;
if (peer == sys_peer)
peer->hpoll = sys_poll;
else
peer->hpoll = peer->minpoll;
}
}
#if 0
static void
usno_timeout(
struct peer *peer
)
{
register struct usnounit *up;
struct refclockproc *pp;
int fd;
char device[20];
char lockfile[128], pidbuf[8];
int dtr = TIOCM_DTR;
pp = peer->procptr;
up = (struct usnounit *)pp->unitptr;
if (up->state) {
if (up->state != 1) {
usno_disc(peer);
return;
}
if (strcmp(pp->a_lastcode, "OK") != 0)
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_NOTICE, "clock %s USNO no modem status",
ntoa(&peer->srcadr));
(void)usno_write(peer, PHONE);
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_NOTICE, "clock %s USNO calling %s\n",
ntoa(&peer->srcadr), PHONE);
up->state = 2;
up->pollcnt++;
pp->polls++;
peer->nextdate = current_time + ANSWER;
return;
}
switch (peer->ttlmax) {
case MODE_MANUAL:
up->run = 0;
break;
case MODE_AUTO:
if (!up->run)
pp->sloppyclockflag |= CLK_FLAG1;
up->run = 1;
break;
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)
msyslog(LOG_NOTICE,
"clock %s USNO backup started ",
ntoa(&peer->srcadr));
}
} else if (up->run && sys_peer->sstclktype != CTL_SST_TS_TELEPHONE) {
peer->hpoll = peer->minpoll;
up->run = 0;
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_NOTICE,
"clock %s USNO backup stopped",
ntoa(&peer->srcadr));
}
break;
default:
msyslog(LOG_ERR,
"clock %s USNO invalid mode", ntoa(&peer->srcadr));
}
if (!(pp->sloppyclockflag & CLK_FLAG1)) {
up->pollcnt = 0;
peer->nextdate = current_time + IDLE;
return;
}
(void)sprintf(lockfile, LOCKFILE, up->unit);
fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644);
if (fd < 0) {
msyslog(LOG_ERR, "clock %s USNO port busy",
ntoa(&peer->srcadr));
return;
}
sprintf(pidbuf, "%d\n", (int) getpid());
write(fd, pidbuf, strlen(pidbuf));
close(fd);
(void)sprintf(device, DEVICE, up->unit);
if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS))) {
unlink(lockfile);
return;
}
if (ioctl(fd, TIOCMBIC, (char *)&dtr) < 0)
msyslog(LOG_WARNING, "usno_timeout: clock %s: couldn't clear DTR: %m",
ntoa(&peer->srcadr));
pp->io.clock_recv = usno_receive;
pp->io.srcclock = (caddr_t)peer;
pp->io.datalen = 0;
pp->io.fd = fd;
if (!io_addclock(&pp->io)) {
(void) close(fd);
unlink(lockfile);
free(up);
return;
}
if (!usno_write(peer, MODEM_SETUP)) {
msyslog(LOG_ERR, "clock %s USNO couldn't write",
ntoa(&peer->srcadr));
io_closeclock(&pp->io);
unlink(lockfile);
free(up);
return;
}
if (up->pollcnt) {
refclock_report(peer, CEVNT_TIMEOUT);
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_NOTICE,
"clock %s USNO calling program terminated",
ntoa(&peer->srcadr));
pp->sloppyclockflag &= ~CLK_FLAG1;
up->pollcnt = 0;
#ifdef DEBUG
if (debug)
printf("usno: calling program terminated\n");
#endif
usno_disc(peer);
return;
}
if (ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr) < -1)
msyslog(LOG_INFO, "usno_timeout: clock %s: couldn't set DTR: %m",
ntoa(&peer->srcadr));
up->state = 1;
peer->nextdate = current_time + WAIT;
}
#endif
static void
usno_disc(
struct peer *peer
)
{
register struct usnounit *up;
struct refclockproc *pp;
int dtr = TIOCM_DTR;
char lockfile[128];
pp = peer->procptr;
up = (struct usnounit *)pp->unitptr;
if (ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr) < 0)
msyslog(LOG_INFO, "usno_disc: clock %s: couldn't clear DTR: %m",
ntoa(&peer->srcadr));
if (up->state > 0) {
up->state = 0;
msyslog(LOG_NOTICE, "clock %s USNO call failed %d",
ntoa(&peer->srcadr), up->state);
#ifdef DEBUG
if (debug)
printf("usno: call failed %d\n", up->state);
#endif
}
io_closeclock(&pp->io);
sprintf(lockfile, LOCKFILE, up->unit);
unlink(lockfile);
peer->nextdate = current_time + WAIT;
}
#if 0
static int
usno_write(
struct peer *peer,
const char *str
)
{
register struct usnounit *up;
struct refclockproc *pp;
int len;
int code;
char cr = '\r';
pp = peer->procptr;
up = (struct usnounit *)pp->unitptr;
len = strlen(str);
#ifdef DEBUG
if (debug)
printf("usno: 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);
}
#endif
#else
int refclock_usno_bs;
#endif