source: trunk/athena/etc/gettime/gettime.c @ 11047

Revision 11047, 5.2 KB checked in by ghudson, 27 years ago (diff)
Use unsigned characters for the time buffer, or we can lose badly reconstructing the time value.
Line 
1/*
2 * gettime
3 *
4 * Retrieve the time from a remote host and print it in ctime(3)
5 * format. The time is acquired via the protocol described in
6 * RFC868. The name of the host to be queried is specified on the
7 * command line, and if the -s option is also specified, and gettime
8 * is run as root, the clock on the local workstation will also be
9 * set. gettime makes five attempts at getting the time, each with
10 * a five second timeout.
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
17#include <time.h>
18#include <errno.h>
19#include <sys/types.h>
20#include <sys/socket.h>
21#include <sys/time.h>
22#include <netinet/in.h>
23#include <netdb.h>
24
25static char rcsid[] = "$Id: gettime.c,v 1.14 1998-01-20 23:05:48 ghudson Exp $";
26
27#define UNIX_OFFSET_TO_1900 ((70 * 365UL + 17) * 24 * 60 * 60)
28#define TIME_SERVICE "time"
29#define MAX_TRIES 5
30#define TIMEOUT 5
31
32char *program_name;
33
34void myperror(char *what, int errnum)
35{
36  fprintf(stderr, "%s: %s: %s\n", program_name, what, strerror(errnum));
37  exit(1);
38}
39
40int main(int argc, char **argv)
41{
42  struct servent *service_info;
43  struct hostent *host_info = NULL;
44  struct sockaddr_in time_address;
45  char *time_hostname;
46  unsigned char buffer[20];
47  int tries, time_socket, result;
48  fd_set read_set;
49  struct timeval timeout, current_time;
50  struct timezone current_timezone;
51  time_t now;
52  int c, setflag = 0, errflag = 0;
53  int granularity = 0;
54
55  /* Set up our program name. */
56  if (argv[0] != NULL)
57    {
58      program_name = strrchr(argv[0], '/');
59      if (program_name != NULL)
60        program_name++;
61      else
62        program_name = argv[0];
63    }
64  else
65    program_name = "gettime";
66
67  /* Parse arguments. */
68  while ((c = getopt(argc, argv, "sg:")) != EOF)
69    {
70      switch (c)
71        {
72        case 's':
73          setflag = 1;
74          break;
75        case 'g':
76          granularity = atoi(optarg);
77          break;
78        case '?':
79          errflag = 1;
80          break;
81        }
82    }
83
84  if (errflag || optind + 1 != argc)
85    {
86      fprintf(stderr, "usage: %s [-g granularity] [-s] hostname\n",
87              program_name);
88      exit(1);
89    }
90
91  time_hostname = argv[optind];
92
93  /* Look up port number for time service. */
94  service_info = getservbyname(TIME_SERVICE, "udp");
95  if (service_info == NULL)
96    {
97      fprintf(stderr, "%s: " TIME_SERVICE "/udp: unknown service\n",
98              program_name);
99      exit(1);
100    }
101
102  /* Resolve hostname (try MAX_TRIES times). We do this in case the
103   * nameserver is just starting up (as in, everyone is just rebooting
104   * from a long power failure, and we are requesting name resolution
105   * before the server is ready).
106   */
107  for (tries = 0; tries < MAX_TRIES; tries++)
108    {
109      host_info = gethostbyname(time_hostname);
110      if (host_info != NULL || (host_info == NULL && h_errno != TRY_AGAIN))
111        break;
112    }
113
114  if (host_info == NULL)
115    {
116      fprintf(stderr, "%s: host %s unknown\n", program_name, time_hostname);
117      exit(1);
118    }
119
120  if (host_info->h_addrtype != AF_INET)
121    {
122      fprintf(stderr, "%s: can't handle address type %d\n",
123              program_name, host_info->h_addrtype);
124      exit(1);
125    }
126
127  /* Grab a socket. */
128  time_socket = socket(AF_INET, SOCK_DGRAM, 0);
129  if (time_socket < 0)
130    myperror("socket", errno);
131
132  /* Set up destination address. */
133  time_address.sin_family = AF_INET;
134  memcpy(&time_address.sin_addr, host_info->h_addr,
135         sizeof(time_address.sin_addr));
136  time_address.sin_port = service_info->s_port;
137
138  /* "Connect" the UDP socket to the time server address. */
139  if (connect(time_socket, (struct sockaddr *)&time_address,
140              sizeof(time_address)) < 0)
141    myperror("connect", errno);
142
143  /* Initialize info for select. */
144  FD_ZERO(&read_set);
145
146  /* Attempt to acquire the time MAX_TRIES times at TIMEOUT interval. */
147  for (tries = 0; tries < MAX_TRIES; tries++)
148    {
149      /* Just send an empty packet. */
150      send(time_socket, buffer, 0, 0);
151
152      /* Wait a little while for a reply. */
153      FD_SET(time_socket, &read_set);
154      timeout.tv_sec = TIMEOUT;
155      timeout.tv_usec = 0;
156      result = select(time_socket + 1, &read_set, NULL, NULL, &timeout);
157
158      if (result == 0)          /* timed out, resend... */
159        continue;
160
161      if (result < 0)           /* error from select */
162        myperror("select", errno);
163
164      /* There must be data available. */
165      result = recv(time_socket, buffer, sizeof(buffer), 0);
166      if (result < 0)
167        myperror("recv", errno);
168
169      /* We should have received exactly four bytes in the packet. */
170      if (result == 4)
171        break;
172
173      fprintf(stderr, "%s: received %d bytes, expected 4\n", program_name,
174              result);
175      exit(1);
176    }
177
178  if (tries == MAX_TRIES)
179    {
180      fprintf(stderr, "%s: Failed to get time from %s.\n", program_name,
181              time_hostname);
182      exit(1);
183    }
184
185  /* Convert RFC868 time to Unix time, and print it. */
186  now = ((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3])
187    - UNIX_OFFSET_TO_1900;
188  fprintf(stdout, "%s", ctime(&now));
189
190  /* Set the time if requested. */
191  if (setflag)
192    {
193      gettimeofday(&current_time, &current_timezone);
194
195      if (current_time.tv_sec < now - granularity
196          || current_time.tv_sec >= now + granularity)
197        {
198          current_time.tv_sec = now;
199          current_time.tv_usec = 0;
200          if (settimeofday(&current_time, &current_timezone) < 0)
201            myperror("settimeofday", errno);
202        }
203    }
204
205  exit(0);
206}
Note: See TracBrowser for help on using the repository browser.