/*
 * time_set.c
 * R.E. Schmidt, TSTS, USNO, May,1992
 * Revised Aug. 1993 RES
 * Obtain system time from a TCP networked server system.
 * Usage:  time_set <server name>
 *     where <server name> is the host name of the server.
 *
 * This routine is the client which request service from the remote
 * "timesrv server".  It creates a connection, sends a time request,
 * shuts down the connection in one direction to signal the
 * server about the end of data, and then receives a 4-byte time signal.
 *
 * <server name> is the hostname of a server providing the time service
 * on port 22375.   The program time_set uses gethostbyname()
 * to obtain an INTERNET address from <server name>.
 * The host on which the server will be running must have the same
 * port number in its /etc/services file:
 *
 * timesrv   22375/tcp                 #time server process
 *
 * The /etc/cron process should be stopped while resetting system time.
 * The script /usr/local/bin/TIME_SET provides this function.
 * This program must be run with root privilege, as it calls stime().
 *
 * See also:
 *
 *   http://tycho.usno.navy.mil/
 *   tick.usno.navy.mil
 *   tock.usno.navy.mil
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <netdb.h>
#include <time.h>
#include <netinet/in.h>
#include <string.h>
#ifdef AMIGA
  #include <exec/types.h>
  #include <exec/io.h>
  #include <devices/timer.h>
  #include <stdlib.h>
  #include <proto/exec.h>
#endif
#ifdef SUN
  #include <sys/time.h>
#endif

#define LEN     sizeof(long)

int s;                          /* connected socket descriptor */
struct hostent *rh;             /* pointer to host info for remote host */
struct servent *sport;          /* pointer to service information */

struct sockaddr_in pad_in;      /* for peer socket address */

void main(argc,argv)
  int argc;
  char *argv[];
{
  auto int xcode = 1;
  auto long tsecs;              /* Seconds since Epoch on server host */

  #ifdef AMIGA
    auto struct timerequest *TimerIO;
    auto struct MsgPort     *TimerMP;
  #endif

  #ifdef SUN
    auto struct timezone tzp;
    auto struct timeval local;
  #endif

  if (argc == 2)
  {
    /* clear out address structures
     */
    memset((char *) &pad_in, 0, sizeof(struct sockaddr_in));

    /* Set up the peer address to which we will connect.
     */
    pad_in.sin_family = AF_INET;

    /* Get the host information for the hostname that the user passed in.
     */
    rh = gethostbyname(argv[1]);

    if (rh != NULL)
    {
      pad_in.sin_addr.s_addr = ((struct in_addr *) (rh->h_addr))->s_addr;

      /* Find the information for the "timesrv" server in order to get the needed
       * port number.
       */
      sport = getservbyname("timesrv", "tcp");

      if (sport != NULL)
      {
        pad_in.sin_port = sport->s_port;

        /* Create the socket.
         */
        s = socket(AF_INET, SOCK_STREAM, 0);

        if (s != -1)
        {
          /* Try to connect to the remote server at the address which was just built
           * into pad_in.
           */
          if (connect(s, (struct sockaddr const *) &pad_in, sizeof(struct sockaddr_in)) != -1)
          {
            /*   Now set the system time:
             */
            #ifdef AMIGA
              if (TimerMP = CreatePort(0,0))
              {
                if (TimerIO = (struct timerequest *) CreateExtIO(TimerMP,sizeof(struct timerequest)))
                {
                  /* Open with UNIT_VBLANK, but any unit can be used
                   */
                  if (!(xcode = OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *) TimerIO,0L)))
                  {
                    /* Setup the command for an IO request.
                     */
                    TimerIO->tr_time.tv_micro   = 0;
                    TimerIO->tr_node.io_Command = TR_SETSYSTIME;

                    if (recv(s, (unsigned char *) &tsecs, LEN, 0) != -1)
                    {
                      /* Issue the set command and wait for it to finish.
                       * NOTE: Time must be adjusted from 1 Jan 1970 to 1 Jan 1978
                       *       and for the local timezone.
                       */
                      TimerIO->tr_time.tv_secs  = (ntohl(tsecs) - (2922 * 24 * 60 * 60) - __timezone);
                      DoIO((struct IORequest *) TimerIO);
                    }
                    else
                    {
                      perror(argv[0]);
                      fprintf(stderr, "%s: error reading result\n", argv[0]);
                    }

                    CloseDevice((struct IORequest *) TimerIO);
                  }
                  else
                    fprintf(stderr, "Could not open timer device\n");

                  DeleteExtIO((struct IORequest *) TimerIO);
                }
                else
                  fprintf(stderr, "Could not create I/O structure\n");

                DeletePort(TimerMP);
              }
              else
                fprintf(stderr, "Could not create port\n");
            #else
              #ifdef SUN
                if (recv(s, (unsigned char *) &tsecs, LEN, 0) != -1)
                {
                  /* Sun does not have the far simpler stime command.
                   */
                  gettimeofday(&local, &tzp);
                  local.tv_sec  = ntohl(tsecs);
                  local.tv_usec = 0;
                  xcode         = settimeofday(&local, &tzp);
                }
              #else
                if (recv(s, (unsigned char *) &tsecs, LEN, 0) != -1)
                {
                  tsecs = ntohl(tsecs);
                  xcode = stime(&tsecs);
                }
              #endif
                else
                {
                  perror(argv[0]);
                  fprintf(stderr, "%s: error reading result\n", argv[0]);
                }
            #endif

            if (xcode)
            {
              perror(argv[0]);
              fprintf(stderr, "System time set failed\n");
              fprintf(stderr, "Bad time value %ld \n", tsecs);
            }

            if (shutdown(s, 2) != -1)
              xcode = 0;
            else
            {
              perror(argv[0]);
              fprintf(stderr, "%s: unable to shutdown socket\n", argv[0]);
            }
          }
          else
          {
            perror(argv[0]);
            fprintf(stderr, "%s: unable to connect to remote\n", argv[0]);
          }
        }
        else
        {
          perror(argv[0]);
          fprintf(stderr, "%s: unable to create socket\n", argv[0]);
        }
      }
      else
        fprintf(stderr, "%s: timesrv not found in /etc/services\n",argv[0]);
    }
    else
      fprintf(stderr, "%s: %s not found in /etc/hosts\n", argv[0], argv[1]);
  }
  else
  {
    fprintf(stderr, "Usage: %s <remote host>\n", argv[0]);
    fprintf(stderr, "Hosts: tick.usno.navy.mil\n");
    fprintf(stderr, "       tock.usno.navy.mil\n");
  }

  exit(xcode);
}
