Listing 1: setime.c -- sets the system date/time retrieved from a server
in TCP/IP-based network

/*
 * This program sets the system date/time retrieved from a server in a TCP/IP
 * based network. 
 * 
 * Usage:  setime <node name> <time interval in hours>
 *
 * 1)  Setup the datagram socket
 * 2)  Get the hostname
 * 3)  Set the optional time interval
 * 4)  Send the daytime datagram to the server
 * 5)  Set the timeout alarm
 * 6)  Receive the daytime string back from the server
 * 7)  Time out if a response doesn't arrive in 20 seconds
 * 8)  Parse the returned date time string
 * 9)  Set the time tm structure
 *10)  If the new time is within range parameter, set the system 
 *     date and time
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 
#include <string.h>
#include <time.h>
#include <errno.h>
#include <signal.h>

#define BUFSZ  256
#define SERVICE "daytime"
#define is_uppercase(x) ((x >= 'A' && x <= 'Z') ? 1 : 0)
#define to_lowercase(x) ((is_uppercase(x)) ? x + 'a' - 'A' : x)

main(argc, argv)
int argc;
char **argv;
{
int s, n, len, dayno, mono, hrno, minno, yearno, secno, chkno, chksec; 
double timedif;
char buf[BUFSZ], *daystr, *monstr, *nostr, *timestr, *yearstr, *hrstr, *minstr,
  *secstr, *chkhrstr;
struct hostent  *hp;
struct servent  *sp;
struct sockaddr_in sin;
time_t tloc, caltime;
struct tm *old, new;
extern int timedout(); 

   /* get a datagram in the Internet domain.  */
   if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
      {
      fprintf(stderr, "socket error");
      exit (1);
      }

   /* lookup the port number of the daytime service */
   if ((sp = getservbyname(SERVICE, "udp")) == NULL)
      {
      fprintf(stderr, "upd %s: unknown service.\n", SERVICE);
      exit (1);
      }

   /* get the server from the command line */
   if ((hp = gethostbyname(*++argv)) == NULL)
      {
      fprintf(stderr, "%s: server unknown.\n", *argv);
      exit (1);
      }

   /* get the time range parameter */
   if ((chkhrstr = *++argv) == NULL)
      {
      chkno = 24;
      chksec = 86400;  /* seconds in 24 hours */
      }
   else /* default to 24 hours */
      {
      chkno = atoi(chkhrstr);
      chksec = chkno * 3600; /* seconds in an an hour */
      if(chksec < 0)
         chksec = chksec * (-1);
      }
 
   /* build the address of the server on the client machine */
   sin.sin_family = AF_INET;
   sin.sin_port = sp->s_port;
   bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);

   /* send datagram to server */
   if (sendto(s, buf, BUFSZ, 0, &sin, sizeof(sin)) < 0)
      {
      fprintf(stderr, "sendto error");
      exit (1);
      }

   /* set the alarm for 20 seconds */
   signal(SIGALRM, timedout);
   alarm(20);
   /* receive a datagram */
   len = sizeof(sin);
   n = recvfrom(s, buf, sizeof(buf), 0, &sin, &len);
   alarm(0);  /* turn off the alarm */
   if (n < 0)
      {
      fprintf(stderr, "recvfrom error");
      exit (1);
      }

   /* terminate the returned datagram string */
   buf[n] = NULL;
   close(s);

   /* parse the returned date string */
   daystr =   strtok(buf," ");
   monstr =   strtok(NULL," ");
   nostr =    strtok(NULL," ");
   timestr =  strtok(NULL," ");
   yearstr =  strtok(NULL," ");
   hrstr =    strtok(timestr,":");
   minstr =   strtok(NULL,":");
   secstr =   strtok(NULL,":");

   /* convert date and time strings to integer */
   dayno = atoi(nostr);
   hrno = atoi(hrstr);
   minno = atoi(minstr);
   secno = atoi(secstr);
   yearno = atoi(yearstr);
   mono = retmonth(monstr);

   /* set the time */
   time(&tloc);
   old = localtime(&tloc);
   new.tm_year = yearno - 1900;
   new.tm_mon = mono - 1;
   new.tm_mday = dayno;
   new.tm_hour = hrno;
   new.tm_min = minno; 
   new.tm_sec = secno;
   new.tm_isdst = old->tm_isdst;
   caltime = mktime(&new);

   timedif = difftime(tloc, caltime);
   if(timedif < 0) /* don't care if time change is negative */
      timedif = timedif * (-1); 

   /* only change, if the time delta is within the parameter range */
   if(timedif <= chksec)
      {
      if(stime(&caltime))
         {
         if(errno == EPERM)
            fprintf(stderr, "You must be super user to set time\n");
         else
            fprintf(stderr, "stime() function error is %d\n", errno);
         }
      }
   else
      fprintf(stderr, "change ignored! range parameter is over %d hours\n", chkno);

   exit(0);
}

/*
 * this function terminates the utility if the datagram doesn't 
 * return in the alloted time
 */
int timedout()
{
   fprintf(stderr, "setime timed out.\n");
   exit (1);
}

/*
 * this function takes a month string, converts it to lowercase and 
 * returns an integer corresponding to the number of the month or zero 
 * for an error
 */
int retmonth(mstr)
char *mstr;
{
int i;

   for(i=0; i < strlen(mstr); i++)  /*month string to lowercase*/
      mstr[i] = to_lowercase(mstr[i]);

   if(strcmp(mstr,"jan") == 0)
      return 1;
   if(strcmp(mstr,"feb") == 0)
      return 2;
   if(strcmp(mstr,"mar") == 0)
      return 3;
   if(strcmp(mstr,"apr") == 0)
      return 4;
   if(strcmp(mstr,"may") == 0)
      return 5;
   if(strcmp(mstr,"jun") == 0)
      return 6;
   if(strcmp(mstr,"jul") == 0)
      return 7;
   if(strcmp(mstr,"aug") == 0)
      return 8;
   if(strcmp(mstr,"sep") == 0)
      return 9;
   if(strcmp(mstr,"oct") == 0)
      return 10;
   if(strcmp(mstr,"nov") == 0)
      return 11;
   if(strcmp(mstr,"dec") == 0)
      return 12;
   return 0;  /*error*/
}

