/* * synctime.c * * A program to synchronize the time of the local machine with that of * the remote time server. * * Copyright (c) 2001-2003 Wu Yongwei. All rights reserved. * * Last updated on 16 September 2003 * */ #include #include #include #include #include #include #include #include #include #include #include #define DEFAULTTIMESERVER "129.6.15.29" /* time-b.nist.gov */ #define INVALID_SOCKET (-1) /* borrowed from Windows */ #ifdef __CYGWIN__ #define timezone ((long)_timezone) /* for Cygwin compatibility */ #endif char *progname = "synctime"; int uflag, vflag; int accuracy; void reporterror(const char *msg) { fprintf(stderr, "%s: %s\n", progname, msg); } u_int32_t resolvehost(const char *hostname) { struct in_addr addr; struct hostent *hostent_ptr; if ( (addr.s_addr = inet_addr(hostname)) == INADDR_NONE) { if (!(hostent_ptr = gethostbyname(hostname))) { return INADDR_NONE; } memcpy(&addr, hostent_ptr->h_addr, hostent_ptr->h_length); } return addr.s_addr; } int main(int argc, char *argv[]) { u_int32_t timeserver; char buf[512], optch; int connfd; int len, n, diff; time_t to; char *timeline; unsigned tmp, year, mon, mday, hour, min, sec, health; struct sockaddr_in servaddr; struct tm when; struct timeval tv; /* Initialize */ uflag = 0; vflag = 0; accuracy = 1; timeserver = inet_addr(DEFAULTTIMESERVER); /* Get command-line options */ while ( (optch = getopt(argc, argv, "huvA:S:")) != EOF) { switch (optch) { case 'h': printf("Usage: %s [-huv] [-A accuracy] [-S timeserver]\n", progname); puts (" h : help"); puts (" u : auto-update"); puts (" v : verbose"); puts (" A accuracy : specify accuracy required in seconds [ default: 1 ]"); puts (" S timeserver : choose timeserver [ default: " DEFAULTTIMESERVER " ]"); puts ("\nA list of time servers can be got from ftp://time-b.nist.gov/pub/nist-srv.lst"); exit(0); break; case 'u': uflag = 1; break; case 'v': vflag = 1; break; case 'A': accuracy = atoi(optarg); break; case 'S': if ( (timeserver = resolvehost(optarg)) == INADDR_NONE) { fprintf(stderr, "Unknown remote host\n"); exit(1); } break; default: fprintf(stderr, "Type '%s -h' to get help\n", progname); exit(1); } } /* Set time server IP and port */ memset(&servaddr, 0, sizeof servaddr); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = timeserver; servaddr.sin_port = htons(13); /* Connect to time server */ connfd = socket(AF_INET, SOCK_STREAM, 0); if (connfd == INVALID_SOCKET) { reporterror("Socket error"); exit(1); } if (connect(connfd, (struct sockaddr *)&servaddr, sizeof servaddr) < 0) { reporterror("Unable to connect to remote host"); exit(1); } /* Receive data */ len = 0; do { n = recv(connfd, buf + len, sizeof buf - len - 1, 0); len += n; } while (n > 0); /* Close socket */ close(connfd); /* Error handling */ if (len == 0) { reporterror("Remote server does not return any data"); exit(1); } /* Clean extra LFs */ if (len && buf[len - 1] == '\n') { buf[--len] = '\0'; } else { buf[len] = '\0'; } if (buf[0] == '\n') { memmove(buf, buf + 1, len--); } /* Display remote time */ if (!uflag || vflag) { printf("Remote: %s\n", buf); } /* Get and display local-machine time */ tzset(); gettimeofday(&tv, NULL); timeline = ctime(&(tv.tv_sec)); if (!uflag || vflag) { printf("Local machine: %.19s.%.3hu %.4s %+.2d%.2d\n", timeline, (unsigned)(tv.tv_usec / 1000), &timeline[20], (int)(-timezone / 3600), abs(timezone) / 60 % 60); } /* Analyse remote time */ if (sscanf(buf, "%u %u-%u-%u %u:%u:%u %u %u %u", &tmp, &year, &mon, &mday, &hour, &min, &sec, &tmp, &tmp, &health) != 10) { /* * For an explanation of the code format, check * http://www.bldrdoc.gov/timefreq/service/its.htm */ reporterror("Time string format incorrect"); exit(1); } else { /* Check health state */ if (health >= 2) { reporterror("Time server is not fully healthy"); if (uflag) { reporterror("Synchronization will not be performed"); } exit(2); } else if (health == 1) { if (vflag) { puts("Remote time may be in error by up to 5 seconds."); } if (accuracy < 5) { accuracy = 5; } } /* Construct four-digit year from local century data */ if (year < 100) { year += ((timeline[20] - '0') * 10 + (timeline[21] - '0')) * 100; } /* Fill in struct tm */ memset(&when, 0, sizeof when); when.tm_sec = sec; when.tm_min = min; when.tm_hour = hour; when.tm_mday = mday; when.tm_mon = mon - 1; when.tm_year = year - 1900; /* Convert remote time to time_t (in UTC) */ to = mktime(&when) - timezone; /* Calculate difference in seconds */ diff = tv.tv_sec + (tv.tv_usec > 500000 ? 1 : 0) - to; /* Display information and/or do adjustments */ if (abs(diff) < accuracy) { if (!uflag || vflag) { puts("Clocks match!"); } } else { if (!uflag || vflag) { printf("%d second%s %s\n", abs(diff), abs(diff) > 1 ? "s" : "", diff > 0 ? "fast" : "behind"); } tv.tv_sec = to; tv.tv_usec = 0; if (uflag) { if (settimeofday(&tv, NULL) == 0) { if (vflag) { puts("Time synchronized"); } } else { reporterror("root privilege needed to set time"); } } } } return 0; }