/*
 *	$Header: /afs/sipb.mit.edu/project/sipb-athena/repository/src/gettime/gettime.c,v 1.11 1996/01/13 14:19:43 svalente Exp $
 */

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>

#define DEFAULT_TIME_SERVER "kerberos.mit.edu"
#define DEFAULT_SLEEP_TIME  12

#define TM_OFFSET 2208988800UL

char buffer[512];
struct timeval tv;
struct timezone tz;
jmp_buf top_level;
void hiccup (int sig);

int main (int argc, char *argv[])
{
    struct sockaddr_in sin;
    struct servent *sp;
    struct hostent *host;
    int setflg = 0;
    int daemon = 0;
    int hours;
    register int i;
    register int s;
    long hosttime;
    register int *nettime;
    char hostname[64];
    int cc, host_retry;
    extern int h_errno;
    volatile int attempts = 0;
    struct sigaction act, oact;
    sigset_t empty;

    sigemptyset (&empty);
    sigemptyset (&act.sa_mask);
    act.sa_flags = 0;

    strcpy (hostname, DEFAULT_TIME_SERVER);
    for (i = 1;i < argc;i++) {
	if (*argv[i] == '-') {
	    switch (argv[i][1]) {
	    case 's':
		setflg++;
		break;
	      case 'd':
		daemon++;
		hours = atoi (argv[i] + 2);
		if (hours < 1) hours = DEFAULT_SLEEP_TIME;
		break;
	    default:
		fprintf (stderr, "%s: Invalid argument %s\n",
			 argv[0], argv[i]);
		exit (11);
	    }
	}
	else strcpy (hostname, argv[i]);
    }
    sp = getservbyname("time", "udp");
    if (sp == 0) {
	fprintf (stderr, "%s: time/udp: unknown service.\n", argv[0]);
	exit (1);
    } 
    host = NULL;
    host_retry = 0;
    while (host == NULL && host_retry < 5) {
	host = gethostbyname(hostname);
	if (h_errno != TRY_AGAIN) break;
	host_retry++;
    }
    if (host == NULL) {
	fprintf (stderr, "%s: The timeserver host %s is unknown\n",
		 argv[0], hostname);
	exit (2);
    }
    sin.sin_family = host->h_addrtype;
    memmove (&sin.sin_addr, host->h_addr, host->h_length);
    sin.sin_port = sp->s_port;

    /* if running as a daemon, fork and let the child run forever */
    if (daemon) if (fork () != 0) return 0;

    while (1) {

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) {
	    perror ("gettime: socket");
	    exit (3);
	}
	if (connect (s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
	    perror ("gettime: connect");
	    exit (4);
	}
	setjmp(top_level);
	if (attempts++ > 5) {
	    fprintf (stderr, "Failed to get time from %s\n", hostname);
	    exit (10);
	}
	alarm(0);
	act.sa_handler = hiccup;
	sigaction (SIGALRM, &act, &oact);
	alarm(5);
	send (s, buffer, 40, 0); /* Send an empty packet */
	if (gettimeofday (&tv, &tz) < 0) {
	    perror ("gettime: gettimeofday");
	    exit (5);
	}
	cc = recv (s, buffer, 512, 0);
	close (s);
	alarm (0);
	act.sa_handler = SIG_DFL;
	sigaction (SIGALRM, &act, &oact);

	if (cc < 0) {
	    perror("recv");
	    fprintf (stderr, "Failed to get time from %s\n", hostname);
	    exit (7);
	}
	if (cc != 4) {
	    fprintf(stderr, "Protocol error -- received %d bytes; %s\n",
		    cc, "expected 4.");
	    exit(8);
	}
	nettime = (int *)buffer;
	hosttime = (long) ntohl (*nettime) - TM_OFFSET;
	if (! daemon) printf ("%s", ctime(&hosttime));
	(&tv)->tv_sec = hosttime;
	if (setflg) {
	    if (settimeofday (&tv, NULL) < 0) {
		perror ("gettime: settimeofday");
		exit (6);
	    }
	}
	if (! daemon) break;
	sleep (hours * 3600);
    }
    return (0);
}

void hiccup (int sig)
{
    longjmp (top_level,0);
}
