[alt.sources] Getting the time over the phone from NBS

erc@khijol.UUCP (Edwin R. Carp) (01/17/90)

Here's the source for nbs_time.c that I scarfed from archive@mgse.  I'm
posting the source, rather than the binary+source, because by the time I
wake up tomorrow morning, it'll probably have been ported to the Amiga, the
PC, the Mac, and VMS, not to mention MVS, TSO, VM, and a host of others :-)

Enjoy!
----------------------------- nbs_time.c ----------------------------
/* CHK=0x0603 */
/*+-----------------------------------------------------------------------
  SCO XENIX SYSTEM V.2  (Others too?)
  nbs_time.c -- call NBS, get time, hangup quickly, set system time,
  wait for top of minute, execute /etc/setclock to update cmos clock

  Warren H. Tucker, 150 West Lake Drive, Mountain Park, GA  30075
  (404)587-5766

  Note: must be root to execute

  Defined functions:
	create_lock_file(lock_file_name)
	hangup(sig)
	hayes_dial()
	hayes_send_cmd(cmd)
	lclose()
	lgetc(char_rtnd)
	lgetc_timeout(timeout_msec)
	lgets_timeout(lrwt)
	lkill_buf()
	lock_tty()
	lopen()
	lputc(lchar)
	lputs_paced(pace_msec,string)
	lrdchk()
	lset_baud_rate(ioctl_flag)
	lset_parity(ioctl_flag)
	main(argc,argv,envp)
	make_lock_name(ttyname,lock_file_name)
	other_lock_name(first_lock_name)
	to_lower(ch)
	to_upper(ch)
	ulcmpb(str1,str2)
	ulindex(str1,str2)
	unlock_tty()
	usage()
	valid_baud_rate(baud)

Sample execution:
% nbs -
nbs_time
Dialing 1(202)653-0351 ... INT to abort ... CONNECT 1200
'47361 201 020050 UTC'
Connect time 1 second(s)
Time retrieved from standard: Mon Jul 18 22:00:50 1988
Waiting for top of minute:    Mon Jul 18 22:00:51 1988
Waiting for top of minute:    Mon Jul 18 22:00:52 1988
Waiting for top of minute:    Mon Jul 18 22:00:53 1988
Waiting for top of minute:    Mon Jul 18 22:00:54 1988
Waiting for top of minute:    Mon Jul 18 22:00:55 1988
Waiting for top of minute:    Mon Jul 18 22:00:56 1988
Waiting for top of minute:    Mon Jul 18 22:00:57 1988
Waiting for top of minute:    Mon Jul 18 22:00:58 1988
/etc/setclock setting ...  result: 0618220188

------------------------------------------------------------------------*/
/*+:EDITS:*/
/*:07-18-1988-22:07-wht-working! */
/*:07-18-1988-17:27-wht-creation */

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termio.h>

#ifndef ushort
#define ushort	unsigned short
#endif
#ifndef uchar
#define uchar	unsigned char
#endif
#ifndef uint
#define uint	unsigned int
#endif
#ifndef ulong
#define ulong	unsigned long
#endif

char  *lgets_timeout(struct lrwt  *);
char  *other_lock_name(char  *);
char to_lower(char );
char to_upper(char );
int create_lock_file(char  *);
int hayes_dial(void);
int hayes_send_cmd(char  *);
int lgetc_timeout(unsigned long );
int lock_tty(void);
int lopen(void);
int lrdchk(void);
int lset_baud_rate(int );
int main(int ,char  *  *,char  *  *);
int make_lock_name(char  *,char  *);
int ulcmpb(unsigned char  *,unsigned char  *);
int ulindex(char  *,char  *);
int valid_baud_rate(unsigned int );
void hangup(int );
void lclose(void);
void lgetc(char  *);
void lkill_buf(void);
void lputc(char );
void lputs_paced(int ,char  *);
void lset_parity(int );
void unlock_tty(void);

ushort	geteuid();
ushort	getuid();
long	nap(long);
long 	time(long *);
char	*ctime(long *);

typedef struct lrwt	/* param to lgets_timeout in eculine.c */
{
ulong	to1;		/* timeout for 1st character (granularity 20) */
ulong	to2;		/* timeout for each next char (granularity 20) */
int		raw_flag;	/* !=0, rtn full buffer, ==0, rtn filtered hayes result */
char	*buffer;	/* buffer to fill */
int		bufsize;	/* size of buffer */
int		count;		/* from proc, count rcvd */
}	LRWT;

#define	EPOCH		40587					/* UNIX starts JD 2440587, */
#define	leap(y, m)	((y+m-1 - 70%m) / m)	/* also known as 1/1/70 */
#define	TONE		'*'
/* #define	TIME		"\n%05ld %03d %02d%02d%02d UTC" */
#define	TIME		"%05ld %03d %02d%02d%02d UTC"

/* for better source line utilization, frequent use of 'fprintf' and 'stderr'
   warrants the following */
#define pf	printf
#define	ff	fprintf
#define se	stderr
#define so	stdout

/* lopen() and related routines error codes */
#define LOPEN_INVALID	-1		/* for invalid tty name */
#define LOPEN_UNKPID	-2		/* unknown pid using line */
#define LOPEN_LCKERR	-3		/* lock file open error */
#define LOPEN_NODEV		-4		/* device does not exist */
#define LOPEN_OPNFAIL	-5		/* count not open line */
#define LOPEN_ALREADY	-6		/* line already open */

extern char	*revision;		/* ecurev.c temp file from buildrev */
extern char	*numeric_revision;	/*ecunumrev.c */

char	LLCKname[128];		/* lock file name */
char	Ltelno[64];			/* telephone number for remote or null */
char	Lline[64];			/* line name */
int		Liofd;				/* file descriptor for line */
int		Lparity;			/* 0==NONE, 'e' == even, 'o' == odd */
struct termio	Llv;		/* attributes for the line to remote */
uint	Lbaud;				/* baud rate */
ushort	euid;
ushort	uid;

/*+-------------------------------------------------------------------------
    to_upper() / to_lower()
  one would think that these were relatively standard
  types of thing, but MSC/Xenix specifies toupper() to convert to upper
  case if not already and Unix says to adjust without testing,
  so, two stupid little routines here
  ASCII only -- no EBCDIC gradoo here please
--------------------------------------------------------------------------*/
char to_upper(ch)
register char    ch;
{ return( ((ch >= 'a') && (ch <= 'z')) ? ch - 0x20 : ch);
}   /* end of to_upper() */

char to_lower(ch)
register char    ch;
{ return( ((ch >= 'A') && (ch <= 'Z')) ? ch + 0x20 : ch);
}   /* end of to_lower() */

/*+----------------------------------------------------------------------------
    ulcmpb(str1,str) -- Upper/Lower [case insensitive] Compare Bytes

 Returns -1 if strings are equal, else failing character position
 If the second strings terminates with a null and both strings have matched
 character for character until that point, then -1 is returned.
 NOTE:  this is not a test for complete equality of two strings, but allows
 discovery of a string as a substring in a larger containing string.
-----------------------------------------------------------------------------*/
int
ulcmpb(str1,str2)
register unsigned char    *str1;
register unsigned char    *str2;
{
register int     istr;

    for( istr=0 ; ;  ++istr )
    {
        if(str2[istr] == '\0')          /* if second string exhausts, match! */
            return(-1);
        if((str1[istr] == '\0' ) ||
			( to_upper(str1[istr]) != to_upper(str2[istr]) ))
            return(istr);
    }
	/*NOTREACHED*/
} /* end of ulcmpb */

/*+-------------------------------------------------------------------------
    ulindex:  Upper/Lower [case insensitive] Index functioni

  Returns position of 'str2' in 'str1' if found
  If 'str2' is null, then 0 is returned (null matches anything)
  Returns -1 if not found

  uses 'ulcmpb'
--------------------------------------------------------------------------*/
int ulindex(str1,str2)
register char	*str1;	/* the (target) string to search */
register char	*str2;	/* the (comparand) string to search for */
{
register int	istr1 = 0;		/* moving index into str1 */
register char	*mstr = str1;	/* moving string pointer */

    if(str2[0] == '\0')             /* null string matches anything */
        return(0);
	while(1)
    {
        if(*mstr == '\0')           /* if we exhaust target string, flunk */
            return(-1);
        /* Can we find either case of first comparand char in target? */
        if( to_upper(*mstr) == to_upper(str2[0]) )
        {
            /* we have a first char match... does rest of string match? */
            if(ulcmpb(mstr,str2) == -1)         /* if the rest matches, ... */
                return(istr1);                  /* ... return match position */
        }
        /* we did not match this time... increment istr1, mstr and try again */
        ++istr1;
        ++mstr;
    }
}	/* end of ulindex */

/*+-----------------------------------------------------------------------
	hangup(sig) -- terminate program (with comm line cleanup)
------------------------------------------------------------------------*/
void
hangup(sig)
int		sig;
{
void	lclose();

	ff(se,"\n");
	if(Liofd != -1)
		lclose();			/* close line */
	exit(sig);
}	/* end of hangup */

/*+-------------------------------------------------------------------------
	make_lock_name(ttyname,lock_file_name)
--------------------------------------------------------------------------*/
make_lock_name(ttyname,lock_file_name)
char	*ttyname;
char	*lock_file_name;
{
register int itmp;
register char *ttyptr;

	if((itmp = ulindex(ttyname,"/dev/tty")) != 0)
		return(LOPEN_INVALID);

	itmp = ulindex(ttyname,"tty");

	ttyptr = &ttyname[itmp];
	strcpy(lock_file_name,"/usr/spool/uucp/LCK..");
	strcat(lock_file_name,ttyptr);
	return(0);

}	/* end of make_lock_name */

/*+-----------------------------------------------------------------------
	create_lock_file()

  Returns 0 if lock file created,else error codes:
	LOPEN_ if error
    else pid of process currently busy on device
------------------------------------------------------------------------*/
create_lock_file(lock_file_name)
char	*lock_file_name;
{
register int	fd_lockf;
int		pid;
int		old_umask;
int		erc = 0;

	old_umask = umask(0);

	if((fd_lockf = open(lock_file_name,O_CREAT | O_EXCL | O_RDWR,0666)) < 0)
	{		/* file already exists */
		if((fd_lockf = open(lock_file_name,O_RDWR,0666)) < 0)
		{
			erc = LOPEN_LCKERR;
			goto RESTORE_UMASK;
		}
		else if(read(fd_lockf,(char *)&pid,sizeof(pid))) 
		{
			if(kill(pid,0)) 				/* is owner pid already dead? */
			{
				if(errno == ESRCH)			/* this error sez so */
				{
					pid = getpid();			/* so we will use it */
					lseek(fd_lockf,0L,0);
					write(fd_lockf,(char *)&pid,sizeof(pid));
					close(fd_lockf);
					erc = 0;
					goto RESTORE_UMASK;
				}
			} 
			/* owner pid still active with lock */
			close(fd_lockf);
			erc = pid;			/* port is busy */
			goto RESTORE_UMASK;
		}
		else
		{
			close(fd_lockf);
			erc = LOPEN_UNKPID;
			goto RESTORE_UMASK;
		}
	} 
	pid = getpid();
	write(fd_lockf,(char *)&pid,sizeof(pid));

	close(fd_lockf);
	chmod(lock_file_name,0666);

RESTORE_UMASK:
	(void)umask(old_umask);
	return(erc);

}	/* end of create_lock_file */

/*+-------------------------------------------------------------------------
	other_lock_name(first_lock_name)
--------------------------------------------------------------------------*/
char *
other_lock_name(first_lock_name)
char	*first_lock_name;
{
register int itmp;
static char other_lock_name[64];

	strcpy(other_lock_name,first_lock_name);
	itmp = strlen(other_lock_name) - 1;
	if(islower(other_lock_name[itmp]))
		other_lock_name[itmp] = toupper(other_lock_name[itmp]);
	else if(isupper(other_lock_name[itmp]))
		other_lock_name[itmp] = tolower(other_lock_name[itmp]);

	return(other_lock_name);
		
}	/* end of other_lock_name */

/*+-------------------------------------------------------------------------
	lock_tty()
--------------------------------------------------------------------------*/
lock_tty()
{
register int itmp;
struct stat ttystat;

	if(itmp = make_lock_name(Lline,LLCKname))
		return(itmp);

	if(stat(Lline,&ttystat) < 0)
		return(LOPEN_NODEV);

	if(itmp = create_lock_file(LLCKname))
		return(itmp);

	if(itmp = create_lock_file(other_lock_name(LLCKname)))
	{
		unlink(LLCKname);
		LLCKname[0] = 0;
		return(itmp);
	}

}	/* end of lock_tty */

/*+-----------------------------------------------------------------------
	void unlock_tty()
------------------------------------------------------------------------*/
void
unlock_tty()
{

	if(LLCKname[0] == 0)
		return;

	unlink(LLCKname);
	unlink(other_lock_name(LLCKname));

	LLCKname[0] = 0;
}	/* end of unlock_tty */

/*+-------------------------------------------------------------------------
	valid_baud_rate(baud) -- returns (positive) baud rate selector
or -1 if invalid baud rate
--------------------------------------------------------------------------*/
valid_baud_rate(baud)
uint	baud;
{
	switch(baud)
	{
		case 110: return(B110);
		case 300: return(B300);
		case 600: return(B600);
		case 1200: return(B1200);
		case 2400: return(B2400);
		case 4800: return(B4800);
		case 9600: return(B9600);
		case 19200: return(EXTA);
		case 38400: return(EXTB);
		default: return(-1);
	}

}	/* end of valid_baud_rate */

/*+-----------------------------------------------------------------------
	lset_baud_rate(ioctl_flag)

  If 'ioctl_flag' is set,then ioctl(Liofd,TCSETA,&Llv)
  is executed after setting baud rate
------------------------------------------------------------------------*/
lset_baud_rate(ioctl_flag)
int		ioctl_flag;
{
int		baud_selector = valid_baud_rate(Lbaud);

	if(baud_selector < 0)
	{
		ff(se,"invalid baud rate: %u\n",Lbaud);
		ff(se,"valid rates: 110,300,600,1200,2400,4800,9600,19200\n");
		return(1);
	}
	Llv.c_cflag &= ~CBAUD;
	Llv.c_cflag |= baud_selector;

	if(ioctl_flag)
		 ioctl(Liofd,(int)TCSETA,(char *)&Llv);
	return(1);

}	/* end of lset_baud_rate */

/*+-----------------------------------------------------------------------
	lset_parity(ioctl_flag)

  If 'ioctl_flag' is set,then ioctl(Liofd,TCSETA,&Llv)
  is executed after setting parity
------------------------------------------------------------------------*/
void
lset_parity(ioctl_flag)
int		ioctl_flag;
{
	Llv.c_cflag &= ~(CS8 | PARENB | PARODD);
	switch(to_lower(Lparity))
	{
		case 'e':
			Llv.c_cflag |= CS7 | PARENB;
			Llv.c_iflag |= ISTRIP;
			break;
		case 'o':
			Llv.c_cflag |= PARODD | CS7 | PARENB;
			Llv.c_iflag |= ISTRIP;
			break;
		default:
			ff(se,"invalid parity: %c ... defaulting to no parity\n");
		case 0:
		case 'n':
			Llv.c_cflag |= CS8;
			Llv.c_iflag &= ~(ISTRIP);
			Lparity = 0;
			break;
	}			

	if(ioctl_flag)
		 ioctl(Liofd,(int)TCSETA,(char *)&Llv);

}	/* end of lset_parity */

/*+-------------------------------------------------------------------------
	lgetc(char_rtnd)
--------------------------------------------------------------------------*/
void
lgetc(char_rtnd)
char	*char_rtnd;
{

READ_AGAIN:
	errno = 0;
	if(read(Liofd,char_rtnd,1) < 1)
	{
		if(errno == EINTR)			/* if signal interrupted, ... */
			goto READ_AGAIN;
		hangup(254);
	}

}	/* end of lgetc */

/*+-------------------------------------------------------------------------
	lrdchk() -- rdchk(Liofd)
--------------------------------------------------------------------------*/
int
lrdchk()
{
	return(rdchk(Liofd));

}	/* end of lrdchk */

/*+-----------------------------------------------------------------------
	lputc(lchar) -- write lchar to comm line
------------------------------------------------------------------------*/
void
lputc(lchar)
char	lchar;
{
	while(write(Liofd,&lchar,1) != 1)
	{
		if(errno == EINTR)
			continue;
		hangup(255);
	}
}	/* end of lputc */

/*+-----------------------------------------------------------------------
	lputs_paced(pace_msec,string) -- write string to comm line
  with time between each character 
------------------------------------------------------------------------*/
void
lputs_paced(pace_msec,string)
register int pace_msec;
register char	*string;
{
register long msec = (pace_msec) ? (long)pace_msec : (long)20;

	while(*string)
	{
		lputc(*string++);
		nap(msec);
	}

}	/* end of lputs_paced */

/*+-------------------------------------------------------------------------
	char *lgets_timeout(LRWT *)

typedef struct lrwt
{
ulong	to1;
ulong	to2;
int		raw_flag;
char	*buffer;
int		bufsize;
int		count;
}	LRWT;

to1 and to2 are unsigned long values in milliseconds (not
currently supported well under BSD4); to1 is the time to wait
for the first character, to2 the time to wait for subsequent
characters.

if raw_flag 0,     non-printables are stripped from beginning
                   and end of received characters (i.e., modem
                   response reads); NULs discarded, parity stripped
if raw_flag 1,     full raw read buffer returned
if raw_flag 2,     full buffer, NULs discarded, parity stripped

buffer is address to read chars into

bufsize is buffer max size (allowing room for terminating null)
which should be at least 2 if raw_size includes 0x80 bit,
else at least 12 characters if 0x80 omitted.

count is a int which, at return, receives the actual count read

--------------------------------------------------------------------------*/
char *
lgets_timeout(lrwt)
LRWT	*lrwt;
{
register int actual_count = 0;
register char	*cptr = lrwt->buffer;
int		max_count = lrwt->bufsize;
char	*rtn_val;
int		timeout_counter;
int		qc1;
int		qc2;
long	quantum;
long	ltmp;

/* minimum wait is 60 msec */
	if(Lbaud < 300)
		if(lrwt->to2 < 300L) lrwt->to2 = 300L;
	if(Lbaud < 1200)
		if(lrwt->to2 < 200L) lrwt->to2 = 200L;
	else
		if(lrwt->to2 < 60L) lrwt->to2 = 60L;

/* shortest interval */
	ltmp = (lrwt->to1 < lrwt->to2) ? lrwt->to1 : lrwt->to2;

/* calculate wait quantum */
	quantum = ltmp / 10L;				/* try for ten ticks */
	if(quantum < 20L)
		quantum = 20L;
	qc1 = lrwt->to1 / quantum;
	if(!qc1) qc1 = 1L;
	qc2 = lrwt->to2 / quantum;
	if(!qc2) qc2 = 1L;

/* perform the lrtw function
   input: qc1 is first nap count (for first charcters) 
          qc2 is 2nd nap count (for subsequent characters) 
          quantum is the nap period in milliseconds
          cptr is char* to receive read string
          max_count is max number of characters incl null
          lrwt->raw_flag as described above

  output: lrwt->count is actual count of return result
          lrwt->buffer is return read buffer
*/
	max_count--;				/* leave room for null */

	lrwt->raw_flag &= 0x0F;		/* get rid of 0xF0 flags */
	timeout_counter = qc1;		/* first timeout */ 
	*cptr = 0;					/* init result string */
	while(timeout_counter--)
	{
		nap(quantum);
		while(lrdchk())
		{
			lgetc(cptr);
			if(lrwt->raw_flag != 1)
			{
				*cptr &= 0x7F;
				if(*cptr == 0)
					continue;
			}

			*++cptr = 0;
			actual_count++;
			if(--max_count == 0)
				goto READ_LINE_POST_PROCESS;
			timeout_counter = qc2;
		}
	}

READ_LINE_POST_PROCESS:
	if(lrwt->raw_flag)
	{
		lrwt->count = actual_count;
		return(lrwt->buffer);
	}
	cptr = lrwt->buffer;
	while(((*cptr >0) && (*cptr < 0x20)) || (*cptr >= 0x7F))
		cptr++;
	rtn_val = cptr;
	actual_count = 0;
	while(((*cptr &= 0x7F) >= 0x20) && (*cptr <= 0x7E))
	{
		cptr++;
		actual_count++;
	}
	*cptr = 0;
	strcpy(lrwt->buffer,rtn_val);
	lrwt->count = actual_count;
	return(lrwt->buffer);
}	/* end of lgets_timeout */

/*+-------------------------------------------------------------------------
	lgetc_timeout(timeout_msec)

 reads one character from line unless timeout_msec passes with no receipt.
 timeout_msec < 20 msec becomes 20 msec
 return char (raw - parity bit preserved) if received, else -1 if timeout
--------------------------------------------------------------------------*/
int
lgetc_timeout(timeout_msec)
ulong	timeout_msec;
{
LRWT	lr;
char	getc_buf[2];		/* room for one char + null */

	lr.to1 = timeout_msec;
	lr.to2 = timeout_msec;
	lr.raw_flag = 1;		/* full raw read */
	lr.buffer = getc_buf;
	lr.bufsize = sizeof(getc_buf);
	lgets_timeout(&lr);
	return(  (lr.count == 1) ? (int)getc_buf[0] : -1 );

}	/* end of lgetc_timeout */

/*+-------------------------------------------------------------------------
	lkill_buf()
--------------------------------------------------------------------------*/
void
lkill_buf()
{
	ioctl(Liofd,(int)TCFLSH,(char *)2); /* flush input and output */
}	/* end of lkill_buf */

/*+----------------------------------------------------------------------
	lopen()
returns negative LOPEN_ codes if failure else positive pid using line
else 0 if successful open
------------------------------------------------------------------------*/
int
lopen()
{
register int	itmp;

	if(Liofd >= 0)
		return(LOPEN_ALREADY);
	if(itmp = lock_tty())		/* get lock file */
		return(itmp);
	Liofd = open(Lline,O_RDWR,0777);
	if(Liofd < 0)
		return(LOPEN_OPNFAIL);
	else
	{
		ioctl(Liofd,(int)TCGETA,(char *)&Llv);
		Llv.c_iflag = (IGNPAR | IGNBRK | IXOFF );
		Llv.c_cflag |= (CREAD | HUPCL);
		Llv.c_lflag = 0;

		Llv.c_cc[VMIN]   = 1;
		Llv.c_cc[VTIME]  = 1;

		lset_baud_rate(0);		/* do not perform ioctl */
		lset_parity(1);			/* do perform ioctl */
	}

	return(0);

}	/* end of lopen */

/*+-----------------------------------------------------------------------
	lclose()
------------------------------------------------------------------------*/
void
lclose()
{
	if(Liofd < 0)
		return;
	ioctl(Liofd,(int)TCGETA,(char *)&Llv); /* save initial state */
	Llv.c_cflag |= HUPCL;
	ioctl(Liofd,(int)TCSETA,(char *)&Llv);
	close(Liofd);
	Liofd = -1;
	unlock_tty();		/* kill lock file */

}	/* end of lclose */

/*+-------------------------------------------------------------------------
	hayes_send_cmd(cmd)
  0: success (cmd accepted)
 -1: cannot talk to modem
--------------------------------------------------------------------------*/
hayes_send_cmd(cmd)
char	*cmd;
{
register char	*cptr;
int retry = 0;

	cptr = cmd;
	lkill_buf();
	while(1)
	{
		lputc(0x07);	/* something random */
		if(lgetc_timeout(500L) < 0)
		{
			if(retry)
				return(-1);
			retry = 1;
			lputs_paced(0,"ATQ0E1V1\r");
			nap((long)1500);
			lkill_buf();
			continue;
		}
		break;
	}
	while(*cptr)
	{
		lputc(*cptr++);
		if(lgetc_timeout(500L) < 0)
			return(-1);
	}
	lputc('\r');
	if(lgetc_timeout(500L) < 0)
		return(-1);
	return(0);

}	/* end of hayes_send_cmd */

/*+-----------------------------------------------------------------------
	hayes_dial()
returns 1 on success (CONNECT), 
		0 if failure to connect
		-1 if cannot talk to modem
------------------------------------------------------------------------*/
int
hayes_dial()
{
register int itmp;
char	s128[128];
int		rtn_code = -1;	/* assume fail, CONNECT will chg to zero */
int		s7;
LRWT	lr;

	s7 = 30;
	strcpy(s128,"ATV1E1S11=45DT" );
	strcat(s128,Ltelno);

	if(itmp = hayes_send_cmd(s128))
		return(itmp);

/* some modems (ahem, the Hayes 2400) do not accurately honor S7 */
	lr.to1 = s7 * 3 * 1000L;
	lr.to2 = 100L;
	lr.raw_flag = 0;
	lr.buffer = s128;
	lr.bufsize = sizeof(s128);
	ff(se,"Dialing %s ... INT to abort ... ",Ltelno);
	fflush(se);
	lgets_timeout(&lr);
	if(lr.count)
		ff(se,"%s\n",s128);
	if(strncmp(s128,"CONNECT",7) == 0)
		return(1);
	return(0);

}	/* end of hayes_dial */

/*+-------------------------------------------------------------------------
	usage()
--------------------------------------------------------------------------*/
void
usage()
{
	ff(se,"Usage: nbs_time [-][-e][-o][-n][-b#][-t#]\n");
	ff(se,"Defaults 1200-N %s %s\n",Ltelno,Lline);
	ff(se," -        use defaults\n");
	ff(se," -e       even parity\n");
	ff(se," -o       odd parity\n");
	ff(se," -n       no parity\n");
	ff(se," -b#      baud rate\n");
	ff(se," -t#      telephone number\n");
	ff(se," -l<name> line (/dev/tty??)\n");
	exit(253);

}	/* end of usage */

/*+-------------------------------------------------------------------------
	main(argc,argv,envp)

  main() program forks to create rcvr process; then main()
  becomes the xmtr process
------------------------------------------------------------------------*/
main(argc,argv,envp)
int		argc;
char	**argv;
char	**envp;
{
char	*cptr;
int		iargv;
int		swchar;
int		itmp;
LRWT	lr;
char	rd_buf[64];
long	now;
long	julian;
long	connect_time;
int		day_of_year;
int		hour;
int		min;
int		sec;
struct tm *lt;

	setbuf(stderr,NULL);
	setbuf(stdout,NULL);

	ff(se,"nbs_time\n");

/* init line variables */
	strcpy(Lline,"/dev/tty1a");
	strcpy(Ltelno,"1(202)653-0351");
	Liofd = -1;
	Lbaud = 1200;
	Lparity = 0;

	if(argc < 2)
		usage();

	if((argc == 2) && (!strcmp(argv[1],"-")))
		;
	else
	{
		for(iargv = 1; iargv < argc; iargv++)
		{
			if(*argv[iargv] != '-')
				continue;
			switch(*(argv[iargv] + 1))
			{
				case 'e': Lparity = 'e'; break;
				case 'o': Lparity = 'o'; break;
				case 'n': Lparity =  0 ; break;
				case 'b': Lbaud = atoi(argv[iargv] + 2); break;
				case 't': strcpy(Ltelno,argv[iargv] + 2); break;
				case 'l': strcpy(Lline,argv[iargv] + 2); break;
				default:  usage();
			}
		}
	}

	uid = getuid();
	euid = geteuid();
	if((euid == 0) || (uid == 0))	/* if root running or prog text ... */
		nice(-40);
	else
	{
		ff(se,"must be root\n");
		exit(252);
	}

	signal(SIGHUP,hangup);
	signal(SIGQUIT,hangup);
	signal(SIGINT,hangup);
	signal(SIGTERM,hangup);

	if(itmp = lopen())
	{
		switch(itmp)
		{
			case LOPEN_INVALID:
				ff(se,"invalid line name\n"); break;
			case LOPEN_UNKPID:
				ff(se,"unknown pid is using line\n"); break;
			case LOPEN_LCKERR:
				ff(se,"lock file error\n"); break;
			case LOPEN_NODEV:
				ff(se,"line does not exist\n"); break;
			case LOPEN_ALREADY:
				ff(se,"line already open\n"); break;
			case LOPEN_OPNFAIL:
				ff(se,"line open error\n"); break;
			default:
				ff(se,"pid %d using line\n",itmp); break;
		}
		exit(250);
	}

	if(!hayes_dial())
		hangup(1);
	connect_time = time((long *)0);

	for(itmp = 0; itmp < 30; itmp++)
	{
		if(lgetc_timeout(500L) == TONE)
			break;
	}

	lr.to1 = 1100L;
	lr.to2 =  100L;
	lr.raw_flag = 0;	/* full raw read */
	lr.buffer = rd_buf;
	lr.bufsize = sizeof(rd_buf);

	lgets_timeout(&lr);

	fputs("'",stdout);
	fwrite(lr.buffer,1,lr.count,stdout);
	fputs("'\n",stdout);

	lclose();
	fprintf(stdout,"Connect time %ld second(s)\n",
		time((long *)0) - connect_time);

	if(sscanf(lr.buffer,TIME, &julian, &day_of_year, &hour, &min, &sec) != 5)
	{
		ff(se,"garbled result: '%s'\n",lr.buffer);
		exit(240);
	}
	else
	{
		now = (((julian - EPOCH) * 24 + hour) * 60 + min) * 60 + sec;
		if(stime(&now) < 0)
			perror("stime");
		fputs("Time retrieved from standard: ",stdout);
		fputs(ctime(&now), stdout);
		lt = localtime(&now);
		while(lt->tm_sec != 58)
		{
			nap(960L);
			now = time((long *)0);
			fputs("Waiting for top of minute:    ",stdout);
			fputs(ctime(&now), stdout);
			lt = localtime(&now);
		}
		now += 60L;	/* get top of next minute */
		lt = localtime(&now);
/*                                mmddhhmmyy */
/*                                0718213488 */


/*  The following statement was added because of a bug in the original code   */
		lt->tm_mon++;


		sprintf(rd_buf,"/etc/setclock %02d%02d%02d%02d%02d",
			lt->tm_mon,lt->tm_mday,lt->tm_hour,lt->tm_min,lt->tm_year);
		fputs("/etc/setclock setting ... ",stdout);
		system(rd_buf);
		fputs("result: ",stdout);
		system("/etc/setclock");
	}
	exit(0);

}	/* end of main */

/* end of nbs_time.c */

-- 
Ed Carp                 N7EKG/5 (28.3-28.5)     uunet!cs.utexas.edu!khijol!erc
Austin, Texas           (512) 832-5884          "Good tea.  Nice house." - Worf

wht@tridom.uucp (Warren Tucker) (01/17/90)

I just posted to alt.sources cmostime3, a two-part shar, which was an
expansion on my previous program, nbs_time.c.  The whole business was
inspired by comp.sources.misc v03i079.  The original author did all the
hard work.  For nbs_time.c, I just put a bunch of async junk around it
to autodial and otherwise make it a standalone program.  cmostime built
on this to avoid using the inexact /etc/setclock and a few other
goodies.
 
v03i079 contains a GREAT program utc.c which allows your system to mimic
NBS and provide esentially the same service via a local call.  Only the
most ultimate time freaks who've replaced their PC's crystal with an
atomic clock can tell the difference (I'm pretty bad, but not that bad)
:-).

-- 
------------------------------------------------------------------
Warren Tucker, Tridom Corporation      ...!gatech!emory!tridom!wht
home address:                           ...!gatech!kd4nc!n4hgf!wht

bob@MorningStar.Com (Bob Sutterfield) (01/18/90)

In article <899@tridom.uucp> wht@tridom.uucp (Warren Tucker) writes:
   v03i079 contains a GREAT program utc.c which allows your system to
   mimic NBS and provide esentially the same service via a local call.
   Only the most ultimate time freaks who've replaced their PC's
   crystal with an atomic clock can tell the difference (I'm pretty
   bad, but not that bad) :-).

I'd hope that only folks either running a local radio clock, or a low-
stratum NTP peer, would offer their system's time as a community
standard.  However, this would be a really nice service for Internet-
connected folks to offer to their UUCP-only neighbors.

thad@cup.portal.com (Thad P Floryan) (01/19/90)

In article <BOB.90Jan17123008@volitans.MorningStar.Com> by
bob@MorningStar.Com (Bob Sutterfield) to alt.sources,rec.ham-radio, he writes:

"	In article <899@tridom.uucp> wht@tridom.uucp (Warren Tucker) writes:
	   v03i079 contains a GREAT program utc.c which allows your system to
	   mimic NBS and provide esentially the same service via a local call.
	   Only the most ultimate time freaks who've replaced their PC's
	   crystal with an atomic clock can tell the difference (I'm pretty
	   bad, but not that bad) :-).

	I'd hope that only folks either running a local radio clock, or a low-
	stratum NTP peer, would offer their system's time as a community
	standard.  However, this would be a really nice service for Internet-
	connected folks to offer to their UUCP-only neighbors.
"

Is there some ESP going on here?  :-)

I've put together a prototype system that does just what Karl describes.  My
time reference is a HeathKit GC-1000 "Most Accurate Clock" which locks onto
NIST's (formerly NBS') WWV at 5, 10 or 15 MHz to provide +/- 10 mSec Universal
Time Coordinated on its RS-232c port.  I've been testing the setup since:

$ loglook utc
Login ID         First                    Last            Name
--------  ----------------------  ----------------------  --------------------
utc       Sat 14-Oct-89 17:59:07  Thu 18-Jan-90 23:12:28  UTC time service
              ^^^^^^^^^
Note that date; 3 days later my antenna snapped during the earthquake, and I
haven't repaired it ... I'm in Silicon Valley, and the antenna is a huge
inverted "V" whose apex WAS at the top of a 40' tower.   :-(

At the moment, one of my systems calls the US Naval Observatory service in
Washington DC every 2 days to maintain time-sync locally, but once I repair
the antenna AND get some more phone lines installed, this service will be
publicly online and I'll post its availability along with all the software.

I use the service to coordinate time for all my LAN-based systems.  The server
provides a time reference identical to the USNO per:

	*
	jjjjj ddd hhmmss UTC

where:	jjjjj	Julian date modulo 2400000
	ddd	days since beginning of year
	hhmmss	time of day in Universal Time Coordinated

based on a program by Michael Scott Baldwin of AT&T Bell Labs.

If you'd like to install and provide such a service on your system, you need
an accurate time reference (the Heath clock is my recommendation: kit price
from their Winter 1990 Catalog No. 219 is $249.95 for the clock and $49.95 for
the RS-232 interface (see page 28)), and:

$ ls -l /usr/local/bin/utcs*
-rwxr-xr-x  1 thad    users      3156 Oct 14 17:22 /usr/local/bin/utcservice

$ file /usr/local/bin/utcs*
/usr/local/bin/utcservice:	mc68k executable (shared demand paged with \
shared library) -F (0413 demand paged) 

and entries for /etc/passwd:

	utc::50:50:UTC time service:/usr/local/bin:/usr/local/bin/utcservice

and /etc/group:

	utcpub::50:utc

All a client system(s) need do is install the "capture" program and execute
it per:

	# must run this as super-user
	#
	echo "\nutc" | cu thadlabs | /usr/local/bin/utcset -s
	exit 0

from a crontab entry like the following:

	#Mn  Hr Da Mo Da (0=SUN, 1=MON, 2=TUE, 3=WED, 4=THU, 5=FRI, 6=SAT)
	#of  of of of of
	#Hr  Da Mo Yr Wk  Command
	#
	03    3  *  *  *  /bin/su root -c "/usr/local/bin/utcset.sh >/dev/null"

assuming one has a /usr/lib/uucp/Systems (or L.sys) entry for the server.

The above is how I've configured the service to date.  Because some "cu" don't
seem to respond to SIGPIPE properly, I'm planning on generalizing the client
program to itself connect and retrieve the server info, and will post the
whole package when it's complete.  If someone would like to help (my time is
VERY limited) and/or you'd like some more info, feel welcome to contact me:

Thad Floryan [ thad@cup.portal.com (OR) ..!sun!portal!cup.portal.com!thad ]

barton@holston.UUCP (Barton A. Fisk) (01/20/90)

Hey, this program is really spiffy, but what exactly is NBS?
I am assuming it's a standard time sort of system, could some-
one expound a bit for me and others who don't know?

Thanks for the program...

-- 
Barton A. Fisk          | UUCP: {attctc,texbell}vector!holston!barton
PO Box 1781             | (PSEUDO) DOMAIN: barton@holston.UUCP     
Lake Charles, La. 70602 | ----------------------------------------
318-439-5984            | "Let him who is without sin cast the first stone"-JC