Cover V04, I03
Article
Listing 1

may95.tar


A Server-Based Date/Time Synchronizer for TCP/IP

Ed Schaefer

Having a consistent system date/time on each node across a UNIX TCP/IP-based network is often vitally important. A solution is to designate one node as the server (where the date and time are accurately kept) and have every other client node obtain the date/time from this server. While some UNIX vendors provide facilities to perform this function, others do not. This article presents a C utility (setime.c, Listing 1), developed under HP-UX, which obtains the date/time from the server and sets the system date/time on the client.

The pseudo-code for the utility is:

1. Setup the datagram socket.

2. Get the hostname from the command line.

3. Get the optional time range parameter from the command line.

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. Setup the time tm structure.

10. If the new time is within range parameter, set the system date/time.

The Datagram Socket

To share data across a network, you must create a connection or socket from the client to the server. In the internet domain, the two general types of sockets are virtual circuits and the datagram.

Since a datagram sends and receives individual packets of data, it's not guaranteed to deliver. The datagram service my C utility uses is the daytime service, which is supported by most BSD and System 5.3 and greater versions of UNIX. To verify if your variant of UNIX supports the daytime datagram, check the /etc/services file for the following line:

daytime         13/udp

which identifies the datagram by name on port 13. The server, via the daytime datagram, returns the following string:

Fri Dec 16 04:21:39 1994

Armed with this time information, the client can perform a time change.

The Network Connection

The first order of business is to establish the network connection using the socket() function call. The two main arguments to socket() are:

AF_INET -- which establishes the internet domain.

SOCK_DGRAM -- which identifies a datagram socket.

Once the socket call is completed, I use the getservbyname() function call to get the port number of the daytime datagram. The two arguments to this function are:

SERVICE -- which points to the daytime port.

udp -- which identifies the service as a datagram.

The return value of getservbyname() is a pointer to the server entry structure, servent.

I then call gethostbyname() to retrieve the server name from the command line and set the pointer to the host entry structure, hostent. The server name must be a valid entry in the /etc/hosts file.

Before sending the datagram, I build the address of the server on the client using the sockaddr_in structure. Building the address entails setting the internet domain and the daytime service port, then using bcopy() to copy the address of the client to the structure.

Send and Receive the Datagram

The sendto() function call sends the service to the server. The sockaddr_in structure previously defined is the major parameter.

The recvfrom() function call returns the current system date/time from the server. The required date/time string will be returned in the character string, buf.

The alarm() function call terminates the utility if recvfrom() doesn't return in the allotted time (20 seconds). If a network failure occurs, alarm() needs only to call the timedout() function and terminate. As this utility is coded, once alarm() is tripped, the contents of the stack are lost and there is no option but to terminate. (For more information on saving the stack and other network programming issues, refer to Using C on the UNIX System, by David A. Curry.)

Finally, strtok() parses the system date/time returned string from the server, yielding the year, month, day, hour, minute, and second.

UNIX and Time

In his book, The Standard C Library, P. J. Plauger warns that if a time stamp is critical, you should check the vendor's C implementation closely. Although it is wise to heed this warning, almost all UNIX systems track time as the number of seconds since midnight of January 1, 1970 (GMT) the ubiquitous EPOCH.

The time() function call returns the number of seconds since the epoch, in the form of a value of type time_t. In every implementation of UNIX I have seen, time_t is TYPEDEFed as a long integer in the time.h header file.

To break the number of seconds since the epoch into a more readable format, the C compiler provides a time tm strucuture:

struct tm {
int tm_sec;   /* seconds 0 to 59 */
int tm_min;   /* minutes 0 to 59 */
int tm_hour;  /* hours 0 to 23 */
int tm_mday;  /* days 1 to 31 */
int tm_mon;   /* months since january 0 to 11 */
int tm_year;  /* years since 1900 */
int tm_wday;  /* days since sunday 0 to 6 */
int tm_yday;  /* days since january 1, 0 to 365 */
int tm_isdst; /* daylight saving time flag */
};

Ultimately, to change the client system date/time, it's necessary to build a tm structure with the parsed data obtained from the client machine.

Checking and Setting the System Date/Time

The second optional command-line argument to setime.c is the time range parameter, with 24 hours being the default. If the time change between the client and server is more than the range parameter, the change is disallowed.

I build the client tm structure from the parsed data, then change the tm structure to the number of seconds from the epoch. I use the mktime() to obtain the number of seconds since the epoch for this structure.

In order to check for a valid change, I find the difference in seconds between the client and server times. For future portability reasons, I chose not to perform arithmetic on the data types, even though time_t is a long integer. The compiler provides the difftime() function, which returns the number of seconds between two time_t types to a double data type.

Finally, if the system date/time change is within the range parameter, I call stime() to change the system date/time. The parameter to this function is the address of the server time variable previously created by mktime(). The only obvious error checking performed is for a user other than root changing the system date/time.

Conclusion

If your variant of UNIX is BSD or System 5.3 or greater, the utility presented here should change the system date/time as expected. To determine whether it will work for you, consider three questions:

  • Does your TCP/IP-based network support the daytime service?

  • Does your UNIX version support time since the epoch in the described way?

  • Does your version of UNIX allow changing time with the stime() function call?

    If the answer to all three is "yes," setime should work for you.

    References

    Curry, David A. Using C on the UNIX System. Sebastopol, CA: O'Reilly & Associates, 1989.

    Plauger, P. J. The Standard C Library. Englewood Cliffs, NJ: Prentice-Hall, 1992.

    About the Author

    Ed Schaefer is an Informix software developer and UNIX system administrator at jeTECH Data Systems of Moorpark, CA, where he develops Time and Attendance Software. He has been involved with UNIX and Informix since 1987 and was previously head of software development at Marie Callendar Pie Shops of Orange, CA.


     



  •