#include <sys/types.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "strerr.h"
#include "ip.h"
#include "str.h"
#include "byte.h"
#include "substdio.h"
#include "readwrite.h"
#include "select.h"
#include "taia.h"

char outbuf[16];
substdio ssout = SUBSTDIO_FDBUF(write,1,outbuf,sizeof outbuf);

#define FATAL "taiclock: fatal: "
#define WARNING "taiclock: warning: "

void die_usage()
{
  strerr_die1x(100,"taiclock: usage: taiclock ip.ad.dr.ess");
}

char *host;
struct ip_address ipremote;
struct sockaddr_in sa;
int s;

char initdeltaoffset[] = {0,0,0,0,0,2,163,0,0,0,0,0,0,0,0,0};
char initdeltamin[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
char initdeltamax[] = {0,0,0,0,0,5,70,0,0,0,0,0,0,0,0,0};
char initerrmin[] = {255,255,255,255,255,255,255,254,0,0,0,0,0,0,0,0};
char initerrmax[] = {0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0};
struct taia deltaoffset;
struct taia deltamin;
struct taia deltamax;
struct taia errmin;
struct taia errmax;

struct taia ta0;
struct taia ta1;

unsigned char query[32];
unsigned char response[32];
struct taia taremote;

struct taia temp1;
struct taia temp2;

unsigned char adj[16];

void main(argc,argv)
int argc;
char **argv;
{
  struct timeval tvselect;
  fd_set rfds;
  char *x;
  unsigned long u;
  int r;
  int loop;

  taia_unpack(initdeltamin,&deltamin);
  taia_unpack(initdeltamax,&deltamax);
  taia_unpack(initdeltaoffset,&deltaoffset);
  taia_unpack(initerrmin,&errmin);
  taia_unpack(initerrmax,&errmax);

  host = argv[1];
  if (!host) die_usage();
  if (!str_diff(host,"0")) host = "127.0.0.1";
  if (host[ip_scan(host,&ipremote)]) die_usage();

  s = socket(AF_INET,SOCK_DGRAM,0);
  if (s == -1)
    strerr_die2sys(111,FATAL,"unable to create socket: ");

  byte_zero(&sa,sizeof(sa));
  byte_copy(&sa.sin_addr,4,&ipremote);
  x = (char *) &sa.sin_port;
  x[0] = 15;
  x[1] = 174;
  sa.sin_family = AF_INET;

  for (loop = 0;loop < 10;++loop) {
    byte_zero(query,sizeof query);
    query[0] = 'c';
    query[1] = 't';
    query[2] = 'a';
    query[3] = 'i';

    /* XXX: cookie-building time */
    taia_now(&ta0);
    taia_pack(query + 16,&ta0);
    u = getpid();
    query[30] = u; u >>= 8;
    query[31] = u;

    taia_now(&ta0);
    if (sendto(s,query,sizeof query,0,(struct sockaddr *) &sa,sizeof sa) == -1)
      strerr_die2sys(111,FATAL,"unable to send request: ");
    FD_ZERO(&rfds);
    FD_SET(s,&rfds);
    tvselect.tv_sec = 1;
    tvselect.tv_usec = 0;
    if (select(s + 1,&rfds,(fd_set *) 0,(fd_set *) 0,&tvselect) != 1) {
      strerr_warn2(WARNING,"unable to read clock: timed out",0);
      continue;
    }
    r = recv(s,response,sizeof response,0);
    if (r == -1) {
      strerr_warn2(WARNING,"unable to read clock: ",&strerr_sys);
      continue;
    }
    taia_now(&ta1);
    if (   (r != sizeof response)
	|| (response[0] != 's')
	|| byte_diff(query + 1,3,response + 1)
	|| byte_diff(query + 20,12,response + 20)
       ) {
      strerr_warn2(WARNING,"unable to read clock: bad response format",0);
      continue;
    }

    taia_unpack(response + 4,&taremote);
    taia_add(&taremote,&taremote,&deltaoffset);

    taia_add(&temp1,&deltamax,&ta0);
    taia_add(&temp2,&deltamin,&ta0);
    if (taia_less(&taremote,&temp1) && !taia_less(&taremote,&temp2)) {
      taia_sub(&temp1,&taremote,&ta0);
      deltamax = temp1;
    }
    taia_add(&temp1,&deltamax,&ta1);
    taia_add(&temp2,&deltamin,&ta1);
    if (taia_less(&temp2,&taremote) && !taia_less(&temp1,&taremote)) {
      taia_sub(&temp2,&taremote,&ta1);
      deltamin = temp2;
    }
  }

  taia_sub(&temp1,&deltamax,&deltamin);
  if (taia_less(&errmax,&temp1) && taia_less(&temp1,&errmin))
    strerr_die2x(111,FATAL,"time uncertainty too large");

  taia_add(&temp1,&deltamax,&deltamin);
  taia_half(&temp1,&temp1);
  taia_sub(&temp1,&temp1,&deltaoffset);

  taia_pack(adj,&temp1);

  if (substdio_putflush(&ssout,adj,sizeof adj) == -1)
    strerr_die2sys(111,FATAL,"unable to write output: ");
  _exit(0);
}


syntax highlighted by Code2HTML, v. 0.9.1