1/* vi: set sw=4 ts=4: */ 2/* 3 * The Rdate command will ask a time server for the RFC 868 time 4 * and optionally set the system time. 5 * 6 * by Sterling Huxley <sterling@europa.com> 7 * 8 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 9 */ 10//config:config RDATE 11//config: bool "rdate (5.6 kb)" 12//config: default y 13//config: help 14//config: The rdate utility allows you to synchronize the date and time of your 15//config: system clock with the date and time of a remote networked system using 16//config: the RFC868 protocol, which is built into the inetd daemon on most 17//config: systems. 18 19//applet:IF_RDATE(APPLET(rdate, BB_DIR_USR_SBIN, BB_SUID_DROP)) 20 21//kbuild:lib-$(CONFIG_RDATE) += rdate.o 22 23//usage:#define rdate_trivial_usage 24//usage: "[-s/-p] HOST" 25//usage:#define rdate_full_usage "\n\n" 26//usage: "Set and print time from HOST using RFC 868\n" 27//usage: "\n -s Only set system time" 28//usage: "\n -p Only print time" 29 30#include "libbb.h" 31 32enum { RFC_868_BIAS = 2208988800UL }; 33 34static void socket_timeout(int sig UNUSED_PARAM) 35{ 36 bb_simple_error_msg_and_die("timeout connecting to time server"); 37} 38 39static time_t askremotedate(const char *host) 40{ 41 uint32_t nett; 42 int fd; 43 44 /* Timeout for dead or inaccessible servers */ 45 alarm(10); 46 signal(SIGALRM, socket_timeout); 47 48 fd = create_and_connect_stream_or_die(host, bb_lookup_std_port("time", "tcp", 37)); 49 50 if (safe_read(fd, &nett, 4) != 4) /* read time from server */ 51 bb_error_msg_and_die("%s: %s", host, "short read"); 52 if (ENABLE_FEATURE_CLEAN_UP) 53 close(fd); 54 55 /* Convert from network byte order to local byte order. 56 * RFC 868 time is seconds since 1900-01-01 00:00 GMT. 57 * RFC 868 time 2,208,988,800 corresponds to 1970-01-01 00:00 GMT. 58 * Subtract the RFC 868 time to get Linux epoch. 59 */ 60 nett = ntohl(nett) - RFC_868_BIAS; 61 62 if (sizeof(time_t) > 4) { 63 /* Now we have 32-bit lsb of a wider time_t 64 * Imagine that nett = 0x00000001, 65 * current time cur = 0x123ffffffff. 66 * Assuming our time is not some 40 years off, 67 * remote time must be 0x12400000001. 68 * Need to adjust our time by (int32_t)(nett - cur). 69 */ 70 time_t cur = time(NULL); 71 int32_t adjust = (int32_t)(nett - (uint32_t)cur); 72 return cur + adjust; 73 } 74 /* This is not going to work, but what can we do */ 75 return (time_t)nett; 76} 77 78int rdate_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 79int rdate_main(int argc UNUSED_PARAM, char **argv) 80{ 81 time_t remote_time; 82 unsigned flags; 83 84 flags = getopt32(argv, "^" "sp" "\0" "-1"); 85 86 remote_time = askremotedate(argv[optind]); 87 88 /* Manpages of various Unixes are confusing. What happens is: 89 * (no opts) set and print time 90 * -s: set time ("do not print the time") 91 * -p: print time ("do not set, just print the remote time") 92 * -sp: print time (that's what we do, not sure this is right) 93 */ 94 95 if (!(flags & 2)) { /* no -p (-s may be present) */ 96 if (time(NULL) == remote_time) 97 bb_simple_error_msg("current time matches remote time"); 98 else { 99 struct timeval ts; 100 ts.tv_sec = remote_time; 101 ts.tv_usec = 0; 102 xsettimeofday(&ts); 103 } 104 } 105 106 if (flags != 1) /* not lone -s */ 107 printf("%s", ctime(&remote_time)); 108 109 return EXIT_SUCCESS; 110} 111