#include #include #include #include #include #include #include #include #include #include extern const char *__progname; static int mode; #define MODE_ADJ 1 #define MODE_SET 2 #define MODE_BOTH 3 #define MODE_PICK 4 #define MODE_SHOW 5 static int qflag = 0; static const char *arg; static struct timeval now; static struct timeval old; static struct timeval new; static struct timeval delta; /* * Print usage goop. */ static void usage(void) { fprintf(stderr,"Usage: %s [-q] [-a] [-s] [-n] yymmddhhmm.ss\n",__progname); fprintf(stderr,"or %s [-q] [-a] [-s] [-n] hostname\n",__progname); fprintf(stderr,"or %s [-q] [-a] [-s] [-n] numeric-address\n",__progname); fprintf(stderr,"or %s\n",__progname); } /* * Do stuff appropriate when a bad argument string is given. * Currently, this means just print usage goop and exit(1). */ static void badformat(void) __attribute__((__noreturn__)); static void badformat(void) { usage(); exit(1); } /* * Get a time from the argument. If the argument is all digits, * possibly except for a dot before the last two digits, and contains * at least one digit (before the dot, if the dot is present), then * take it as a numeric time. Otherwise, treat it as a hostname or * numeric address, and connect to the time/tcp port on that host and * get the time that way. * * gettv() is the entry point; get_numeric_time and get_network_time * are internal implementation details. */ static int get_numeric_time(const char *s) { struct tm wanted; const char *cp; char t[5]; time_t tmp; int i; wanted = *localtime(&tmp); now.tv_sec = tmp; for (cp=s;(*cp)&&('.'!=*cp);cp++) if (!isdigit((unsigned char)*cp)) return(0); if (cp-s > 8) { int nowyear; i = (cp - s) - 8; if (i > 4) badformat(); bcopy(s,&t[0],i); t[i] = '\0'; nowyear = wanted.tm_year; if (sscanf(&t[0],"%d",&wanted.tm_year) != 1) badformat(); if (wanted.tm_year < 100) { i = (wanted.tm_year + 100 - (nowyear % 100)) % 100; wanted.tm_year = nowyear + i - ((i > 50) ? 100 : 0); } s = cp - 8; } if (cp-s > 6) { i = (cp - s) - 6; bcopy(s,&t[0],i); t[i] = '\0'; if (sscanf(&t[0],"%d",&wanted.tm_mon) != 1) badformat(); s = cp - 6; } if (cp-s > 4) { i = (cp - s) - 4; bcopy(s,&t[0],i); t[i] = '\0'; if (sscanf(&t[0],"%d",&wanted.tm_mday) != 1) badformat(); s = cp - 4; } if (cp-s > 2) { i = (cp - s) - 2; bcopy(s,&t[0],i); t[i] = '\0'; if (sscanf(&t[0],"%d",&wanted.tm_hour) != 1) badformat(); s = cp - 2; } if (cp-s > 0) { i = cp - s; bcopy(s,&t[0],i); t[i] = '\0'; if (sscanf(&t[0],"%d",&wanted.tm_min) != 1) badformat(); s = cp; } if (*cp == '.') { if ( !cp[1] || !cp[2] || cp[3] || !isdigit((unsigned char)cp[1]) || !isdigit((unsigned char)cp[2]) ) return(0); if (sscanf(cp+1,"%d",&wanted.tm_sec) != 1) badformat(); } tmp = mktime(&wanted); if (tmp == (time_t)-1) { fprintf(stderr,"%s: %s: can't represent time (mktime failed)\n",__progname,s); exit(1); } new.tv_usec = 0; new.tv_sec = tmp; return(1); } static int get_network_time(const char *str) { struct addrinfo *ai; struct addrinfo *ai0; struct addrinfo hints; int err; int s; char host[NI_MAXHOST]; char serv[NI_MAXSERV]; char *txt; unsigned char val[4]; time_t v; int l; int r; hints.ai_flags = 0; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; err = getaddrinfo(str,"time",&hints,&ai0); if (err) { fprintf(stderr,"%s: %s: %s\n",__progname,str,gai_strerror(err)); exit(1); } if (! ai0) { fprintf(stderr,"%s: %s: resolved but no addresses?\n",__progname,str); exit(1); } txt = 0; for (ai=ai0;ai;ai=ai->ai_next) { if (txt) free(txt); if (getnameinfo(ai->ai_addr,ai->ai_addrlen,&host[0],NI_MAXHOST,&serv[0],NI_MAXSERV,NI_NUMERICHOST|NI_NUMERICSERV|NI_WITHSCOPEID)) { asprintf(&txt,"af %d, can't get numeric address string (%s)",ai->ai_addr->sa_family,strerror(errno)); } else { txt = strdup(&host[0]); } s = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (s < 0) { fprintf(stderr,"%s: socket (%s): %s\n",__progname,txt,strerror(errno)); continue; } if (connect(s,ai->ai_addr,ai->ai_addrlen) < 0) { fprintf(stderr,"%s: connect (%s): %s\n",__progname,txt,strerror(errno)); nextaddr_close:; close(s); continue; } l = 0; while (l < 4) { r = read(s,&val[l],4-l); if (r < 0) { fprintf(stderr,"%s: read (%s): %s\n",__progname,txt,strerror(errno)); goto nextaddr_close; } if (r == 0) { fprintf(stderr,"%s: premature EOF (%s)\n",__progname,txt); goto nextaddr_close; } l += r; } /* the magic number: # of seconds from 1900-01-01 to 1970-01-01 */ v = (val[0] * 0x01000000UL) + (val[1] * 0x00010000UL) + (val[2] * 0x00000100UL) + (val[3] * 0x00000001UL) - 2208988800UL; new.tv_usec = 0; new.tv_sec = v; printf("time from %s: %s",txt,ctime(&v)); return(1); } return(0); } static void gettv(const char *s) { if (get_numeric_time(s)) return; if (get_network_time(s)) return; fprintf(stderr,"%s: %s: can't get time\n",__progname,s); exit(1); } static void printadj(struct timeval *adj, const char *tag) { if (((long int)adj->tv_sec < 0) || ((adj->tv_sec == 0) && ((long int)adj->tv_usec < 0))) { printf("%s: retard by %d.%06d\n",tag,-(int)adj->tv_sec,-(int)adj->tv_usec); } else { printf("%s: advance by %d.%06d\n",tag,(int)adj->tv_sec,(int)adj->tv_usec); } } int main(int, char **); int main(int ac, char **av) { if (ac > 1) { mode = MODE_PICK; while (1) { ac --; av ++; if (ac < 1) { fprintf(stderr,"%s: need a hostname/address or time\n",__progname); usage(); exit(1); } if (!strcmp(av[0],"-a")) { mode = MODE_ADJ; continue; } if (!strcmp(av[0],"-s")) { mode = MODE_SET; continue; } if (!strcmp(av[0],"-as") || !strcmp(av[0],"-sa")) { mode = MODE_BOTH; continue; } if (!strcmp(av[0],"-n")) { mode = MODE_PICK; continue; } if (!strcmp(av[0],"-q")) { qflag = 1; continue; } if (ac != 1) { fprintf(stderr,"%s: warning: extra argument(s) ignored\n",__progname); usage(); } arg = av[0]; break; } } else { mode = MODE_SHOW; } if (((mode == MODE_SHOW) || !qflag) && (geteuid() != 0)) { fprintf(stderr,"%s: must be root\n",__progname); exit(1); } switch (mode) { case MODE_SHOW: delta.tv_sec = 0; delta.tv_usec = 0; adjtime(&delta,&old); adjtime(&old,(struct timeval *)0); printadj(&old,"old adjustment"); break; default: gettv(arg); gettimeofday(&now,(struct timezone *)0); delta.tv_sec = 0; delta.tv_usec = 0; adjtime(&delta,&old); { long int deltasec; long int deltausec; deltasec = (long int)new.tv_sec - (long int)now.tv_sec; deltausec = (long int)new.tv_usec - (long int)now.tv_usec; if (deltasec > 0) { if (deltausec < 0) { deltasec --; deltausec += 1000000; } } else { if (deltausec > 0) { deltasec ++; deltausec -= 1000000; } } delta.tv_sec = deltasec; delta.tv_usec = deltausec; } if (mode == MODE_PICK) mode = ((delta.tv_sec < -300) || (delta.tv_sec > 300)) ? MODE_SET : MODE_ADJ; switch (mode) { case MODE_SET: if (qflag) { printf("would set time\n"); } else { settimeofday(&new,0); printf("setting time\n"); } break; case MODE_ADJ: if (delta.tv_sec > 2000) delta.tv_sec = 2000; if (qflag) { printadj(&delta,"adjustment would be"); } else { adjtime(&delta,0); printadj(&old,"old adjustment"); printadj(&delta,"new adjustment"); } break; case MODE_BOTH: if ( ((long int)delta.tv_sec == 0) && ((long int)delta.tv_usec >= 0) ) { if (qflag) { printadj(&delta,"subsecond advance (would set time)"); } else { settimeofday(&new,0); printadj(&old,"old adjustment"); printadj(&delta,"new adjustment"); printf("subsecond advance, setting time\n"); } } else { if (delta.tv_sec > 2000) delta.tv_sec = 2000; if (qflag) { printadj(&delta,"adjustment would be"); } else { adjtime(&delta,(struct timeval *)0); printadj(&old,"old adjustment"); printadj(&delta,"new adjustment"); } } break; } } exit(0); }