| 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. 
 
 
 |