3 static char elsieid
[] = "@(#)date.c 7.45";
5 ** Modified from the UCB version with the SCCS ID appearing below.
7 #endif /* !defined NOID */
8 #endif /* !defined lint */
11 * Copyright (c) 1985, 1987, 1988 The Regents of the University of California.
12 * All rights reserved.
14 * Redistribution and use in source and binary forms are permitted
15 * provided that the above copyright notice and this paragraph are
16 * duplicated in all such forms and that any documentation,
17 * advertising materials, and other materials related to such
18 * distribution and use acknowledge that the software was developed
19 * by the University of California, Berkeley. The name of the
20 * University may not be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
24 * WARRANTIES OF MERCHANT[A]BILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 "@(#) Copyright (c) 1985, 1987, 1988 The Regents of the University of California.\n\
30 All rights reserved.\n";
34 static char sccsid
[] = "@(#)date.c 4.23 (Berkeley) 9/20/88";
38 #if HAVE_ADJTIME || HAVE_SETTIMEOFDAY
39 #include "sys/time.h" /* for struct timeval, struct timezone */
40 #endif /* HAVE_ADJTIME || HAVE_SETTIMEOFDAY */
42 #include "utmp.h" /* for OLD_TIME (or its absence) */
48 #define OTIME_MSG "old time"
51 #define NTIME_MSG "new time"
55 ** The two things date knows about time are. . .
59 #define TM_YEAR_BASE 1900
60 #endif /* !defined TM_YEAR_BASE */
64 #endif /* !defined SECSPERMIN */
67 extern char ** environ
;
68 extern char * getlogin();
69 extern time_t mktime();
72 extern char * strchr();
74 extern char * tzname
[2];
76 static int retval
= EXIT_SUCCESS
;
78 static void checkfinal
P((const char *, int, time_t, time_t));
79 static int comptm
P((const struct tm
*, const struct tm
*));
80 static time_t convert
P((const char *, int, time_t));
81 static void display
P((const char *));
82 static void dogmt
P((void));
83 static void errensure
P((void));
84 static void iffy
P((time_t, time_t, const char *, const char *));
85 int main
P((int, char**));
86 static const char * nondigit
P((const char *));
87 static void oops
P((const char *));
88 static void reset
P((time_t, int));
89 static void timeout
P((FILE *, const char *, const struct tm
*));
90 static void usage
P((void));
91 static void wildinput
P((const char *, const char *,
99 register const char * format
;
100 register const char * value
;
101 register const char * cp
;
104 register int aflag
= 0;
105 register int dflag
= 0;
106 register int nflag
= 0;
107 register int tflag
= 0;
108 register int minuteswest
;
109 register int dsttime
;
110 register double adjust
;
115 INITIALIZE(minuteswest
);
120 (void) setlocale(LC_ALL
, "");
121 #endif /* defined(LC_ALL) */
124 (void) bindtextdomain(TZ_DOMAIN
, TZ_DOMAINDIR
);
125 #endif /* defined(TEXTDOMAINDIR) */
126 (void) textdomain(TZ_DOMAIN
);
127 #endif /* HAVE_GETTEXT */
129 format
= value
= NULL
;
130 while ((ch
= getopt(argc
, argv
, "ucnd:t:a:")) != EOF
&& ch
!= -1) {
134 case 'u': /* do it in UTC */
138 case 'n': /* don't set network */
141 case 'd': /* daylight saving time */
143 (void) fprintf(stderr
,
144 _("date: error: multiple -d's used"));
150 if (*cp
== '\0' || *nondigit(cp
) != '\0')
151 wildinput(_("-t value"), optarg
,
152 _("must be a non-negative number"));
154 case 't': /* minutes west of UTC */
156 (void) fprintf(stderr
,
157 _("date: error: multiple -t's used"));
162 minuteswest
= atoi(cp
);
163 if (*cp
== '+' || *cp
== '-')
165 if (*cp
== '\0' || *nondigit(cp
) != '\0')
166 wildinput(_("-d value"), optarg
,
167 _("must be a number"));
169 case 'a': /* adjustment */
171 (void) fprintf(stderr
,
172 _("date: error: multiple -a's used"));
178 if (*cp
== '+' || *cp
== '-')
180 if (*cp
== '\0' || strcmp(cp
, ".") == 0)
181 wildinput(_("-a value"), optarg
,
182 _("must be a number"));
186 if (*nondigit(cp
) != '\0')
187 wildinput(_("-a value"), optarg
,
188 _("must be a number"));
192 while (optind
< argc
) {
198 (void) fprintf(stderr
,
199 _("date: error: multiple formats in command line\n"));
202 else if (value
== NULL
)
205 (void) fprintf(stderr
,
206 _("date: error: multiple values in command line\n"));
212 ** This order ensures that "reasonable" twelve-digit inputs
213 ** (such as 120203042006) won't be misinterpreted
214 ** even if time_t's range all the way back to the thirteenth
215 ** century. Do not change the order.
217 t
= convert(value
, (dousg
= TRUE
), now
);
219 t
= convert(value
, (dousg
= FALSE
), now
);
222 ** Out of range values,
223 ** or time that falls in a DST transition hole?
225 if ((cp
= strchr(value
, '.')) != NULL
) {
227 ** Ensure that the failure of
228 ** TZ=America/New_York date 8712312359.60
229 ** doesn't get misdiagnosed. (It was
230 ** TZ=America/New_York date 8712311859.60
231 ** when the leap second was inserted.)
232 ** The normal check won't work since
233 ** the given time is valid in UTC.
235 if (atoi(cp
+ 1) >= SECSPERMIN
)
236 wildinput(_("time"), value
,
237 _("out of range seconds given"));
240 t
= convert(value
, FALSE
, now
);
242 t
= convert(value
, TRUE
, now
);
243 wildinput(_("time"), value
,
245 _("out of range value given") :
246 _("time skipped when clock springs forward"));
250 ** Entire command line has now been checked.
256 tv
.tv_sec
= (int) adjust
;
257 tv
.tv_usec
= (int) ((adjust
- tv
.tv_sec
) * 1000000L);
258 if (adjtime(&tv
, (struct timeval
*) NULL
) != 0)
260 #endif /* HAVE_ADJTIME */
262 reset((time_t) (now
+ adjust
), nflag
);
263 #endif /* !HAVE_ADJTIME */
265 ** Sun silently ignores everything else; we follow suit.
269 if (dflag
|| tflag
) {
270 #if HAVE_SETTIMEOFDAY == 2
273 if (!dflag
|| !tflag
)
274 if (gettimeofday((struct timeval
*) NULL
, &tz
) != 0)
275 oops("gettimeofday");
277 tz
.tz_dsttime
= dsttime
;
279 tz
.tz_minuteswest
= minuteswest
;
280 if (settimeofday((struct timeval
*) NULL
, &tz
) != 0)
281 oops("settimeofday");
282 #endif /* HAVE_SETTIMEOFDAY == 2 */
283 #if HAVE_SETTIMEOFDAY != 2
284 (void) fprintf(stderr
,
285 _("date: warning: kernel doesn't keep -d/-t information, option ignored\n"));
286 #endif /* HAVE_SETTIMEOFDAY != 2 */
294 checkfinal(value
, dousg
, t
, now
);
301 timeout(stdout
, "%c\n", &tm
);
304 #endif /* defined EBUG */
308 /* gcc -Wall pacifier */
316 static char ** fakeenv
;
318 if (fakeenv
== NULL
) {
322 static char tzegmt0
[] = "TZ=GMT0";
324 for (n
= 0; environ
[n
] != NULL
; ++n
)
326 fakeenv
= (char **) malloc((size_t) (n
+ 2) * sizeof *fakeenv
);
327 if (fakeenv
== NULL
) {
328 (void) perror(_("Memory exhausted"));
333 fakeenv
[to
++] = tzegmt0
;
334 for (from
= 1; environ
[from
] != NULL
; ++from
)
335 if (strncmp(environ
[from
], "TZ=", 3) != 0)
336 fakeenv
[to
++] = environ
[from
];
345 ** We assume we're on a System-V-based system,
347 ** should write System-V-format utmp entries,
348 ** and don't have network notification to worry about.
351 #include "fcntl.h" /* for O_WRONLY, O_APPEND */
356 reset(const time_t newt
, const int nflag
)
357 #else /* !__STDC__ */
361 #endif /* !__STDC__ */
377 ** Wouldn't it be great if stime returned the old time?
380 if (stime(&newt
) != 0)
382 s
.before
.ut_type
= OLD_TIME
;
383 s
.before
.ut_time
= oldt
;
384 (void) strcpy(s
.before
.ut_line
, OTIME_MSG
);
385 s
.after
.ut_type
= NEW_TIME
;
386 s
.after
.ut_time
= newt
;
387 (void) strcpy(s
.after
.ut_line
, NTIME_MSG
);
388 fid
= open(WTMP_FILE
, O_WRONLY
| O_APPEND
);
390 oops(_("log file open"));
391 if (write(fid
, (char *) &s
, sizeof s
) != sizeof s
)
392 oops(_("log file write"));
394 oops(_("log file close"));
396 pututline(&s
.before
);
398 #endif /* !HAVE_UTMPX_H */
400 sx
.before
.ut_type
= OLD_TIME
;
401 sx
.before
.ut_tv
.tv_sec
= oldt
;
402 (void) strcpy(sx
.before
.ut_line
, OTIME_MSG
);
403 sx
.after
.ut_type
= NEW_TIME
;
404 sx
.after
.ut_tv
.tv_sec
= newt
;
405 (void) strcpy(sx
.after
.ut_line
, NTIME_MSG
);
406 #if !SUPPRESS_WTMPX_FILE_UPDATE
407 /* In Solaris 2.5 (and presumably other systems),
408 `date' does not update /var/adm/wtmpx.
409 This must be a bug. If you'd like to reproduce the bug,
410 define SUPPRESS_WTMPX_FILE_UPDATE to be nonzero. */
411 fid
= open(WTMPX_FILE
, O_WRONLY
| O_APPEND
);
413 oops(_("log file open"));
414 if (write(fid
, (char *) &sx
, sizeof sx
) != sizeof sx
)
415 oops(_("log file write"));
417 oops(_("log file close"));
418 #endif /* !SUPPRESS_WTMPX_FILE_UPDATE */
419 pututxline(&sx
.before
);
420 pututxline(&sx
.after
);
421 #endif /* HAVE_UTMPX_H */
424 #endif /* defined OLD_TIME */
428 ** We assume we're on a BSD-based system,
429 ** should use settimeofday,
430 ** should write BSD-format utmp entries (using logwtmp),
431 ** and may get to worry about network notification.
432 ** The "time name" changes between 4.3-tahoe and 4.4;
433 ** we include sys/param.h to determine which we should use.
437 #include "sys/param.h"
439 #define TIME_NAME "date"
440 #endif /* defined BSD4_4 */
443 #endif /* !defined BSD4_4 */
444 #endif /* !defined TIME_NAME */
447 #include "sys/socket.h"
448 #include "netinet/in.h"
451 #include "protocols/timed.h"
453 extern int logwtmp();
455 #if HAVE_SETTIMEOFDAY == 1
456 #define settimeofday(t, tz) (settimeofday)(t)
457 #endif /* HAVE_SETTIMEOFDAY == 1 */
461 #endif /* !defined TSP_SETDATE */
467 register const char * username
;
468 static struct timeval tv
; /* static so tv_usec is 0 */
472 #endif /* defined EBUG */
473 username
= getlogin();
474 if (username
== NULL
|| *username
== '\0') /* single-user or no tty */
478 if (nflag
|| !netsettime(tv
))
479 #endif /* defined TSP_SETDATE */
482 ** "old" entry is always written, for compatibility.
484 logwtmp("|", TIME_NAME
, "");
485 if (settimeofday(&tv
, (struct timezone
*) NULL
) == 0) {
486 logwtmp("{", TIME_NAME
, ""); /* } */
487 syslog(LOG_AUTH
| LOG_NOTICE
, _("date set by %s"),
489 } else oops("settimeofday");
493 #endif /* !defined OLD_TIME */
496 wildinput(item
, value
, reason
)
497 const char * const item
;
498 const char * const value
;
499 const char * const reason
;
501 (void) fprintf(stderr
,
502 _("date: error: bad command line %s \"%s\", %s\n"),
503 item
, value
, reason
);
510 if (retval
== EXIT_SUCCESS
)
511 retval
= EXIT_FAILURE
;
516 register const char * cp
;
518 while (is_digit(*cp
))
526 (void) fprintf(stderr
, _("date: usage is date [-u] [-c] [-n] [-d dst] \
527 [-t min-west] [-a sss.fff] [[yyyy]mmddhhmm[yyyy][.ss]] [+format]\n"));
534 const char * const string
;
538 (void) fprintf(stderr
, _("date: error: "));
540 (void) perror(string
);
542 display((char *) NULL
);
547 const char * const format
;
553 tm
= *localtime(&now
);
554 timeout(stdout
, format
? format
: "%+", &tm
);
555 (void) putchar('\n');
556 (void) fflush(stdout
);
557 (void) fflush(stderr
);
558 if (ferror(stdout
) || ferror(stderr
)) {
559 (void) fprintf(stderr
,
560 _("date: error: couldn't write results\n"));
566 extern size_t strftime();
571 timeout(fp
, format
, tmp
)
573 const char * const format
;
574 const struct tm
* const tmp
;
583 cp
= malloc((size_t) size
);
586 (void) fprintf(stderr
,
587 _("date: error: can't get memory\n"));
592 result
= strftime(cp
, size
, format
, tmp
);
593 if (result
!= 0 || cp
[0] == '\0')
596 cp
= realloc(cp
, (size_t) size
);
598 (void) fwrite(cp
, 1, result
, fp
);
604 register const struct tm
* const atmp
;
605 register const struct tm
* const btmp
;
609 if ((result
= (atmp
->tm_year
- btmp
->tm_year
)) == 0 &&
610 (result
= (atmp
->tm_mon
- btmp
->tm_mon
)) == 0 &&
611 (result
= (atmp
->tm_mday
- btmp
->tm_mday
)) == 0 &&
612 (result
= (atmp
->tm_hour
- btmp
->tm_hour
)) == 0 &&
613 (result
= (atmp
->tm_min
- btmp
->tm_min
)) == 0)
614 result
= atmp
->tm_sec
- btmp
->tm_sec
;
620 ** convert user's input into a time_t.
623 #define ATOI2(ar) (ar[0] - '0') * 10 + (ar[1] - '0'); ar += 2;
627 convert(register const char * const value
, const int dousg
, const time_t t
)
628 #else /* !__STDC__ */
629 convert(value
, dousg
, t
)
630 register const char * const value
;
633 #endif /* !__STDC__ */
635 register const char * cp
;
636 register const char * dotp
;
637 register int cent
, year_in_cent
, month
, hour
, day
, mins
, secs
;
643 year_in_cent
= tm
.tm_year
% DIVISOR
+ TM_YEAR_BASE
% DIVISOR
;
644 cent
= tm
.tm_year
/ DIVISOR
+ TM_YEAR_BASE
/ DIVISOR
+
645 year_in_cent
/ DIVISOR
;
646 year_in_cent
%= DIVISOR
;
647 if (year_in_cent
< 0) {
648 year_in_cent
+= DIVISOR
;
651 month
= tm
.tm_mon
+ 1;
657 dotp
= strchr(value
, '.');
658 for (cp
= value
; *cp
!= '\0'; ++cp
)
659 if (!is_digit(*cp
) && cp
!= dotp
)
660 wildinput(_("time"), value
, _("contains a nondigit"));
663 dotp
= strchr(value
, '\0');
667 wildinput(_("time"), value
,
668 _("seconds part is not two digits"));
675 wildinput(_("time"), value
,
676 _("main part is wrong length"));
680 year_in_cent
= ATOI2(cp
);
688 year_in_cent
= ATOI2(cp
);
691 case 8: /* mmddhhmm */
693 /* fall through to. . . */
696 /* fall through to. . . */
703 year_in_cent
= ATOI2(cp
);
710 year_in_cent
= ATOI2(cp
);
715 tm
.tm_year
= cent
* 100 + year_in_cent
- TM_YEAR_BASE
;
716 tm
.tm_mon
= month
- 1;
723 outt
= mktime(&outtm
);
724 return (comptm(&tm
, &outtm
) == 0) ? outt
: -1;
728 ** Code from here on out is either based on code provided by UCB
729 ** or is only called just before the program exits.
733 ** Check for iffy input.
738 checkfinal(const char * const value
,
742 #else /* !__STDC__ */
743 checkfinal(value
, didusg
, t
, oldnow
)
744 const char * const value
;
748 #endif /* !__STDC__ */
754 register long offset
;
757 ** See if there's both a USG and a BSD interpretation.
759 othert
= convert(value
, !didusg
, oldnow
);
760 if (othert
!= -1 && othert
!= t
)
761 iffy(t
, othert
, value
, _("year could be at start or end"));
763 ** See if there's both a DST and a STD version.
767 othertm
.tm_isdst
= !tm
.tm_isdst
;
768 othert
= mktime(&othertm
);
769 if (othert
!= -1 && othertm
.tm_isdst
!= tm
.tm_isdst
&&
770 comptm(&tm
, &othertm
) == 0)
771 iffy(t
, othert
, value
,
772 _("both standard and summer time versions exist"));
776 ** If a jurisdiction shifts time *without* shifting whether time is
777 ** summer or standard (as Hawaii, the United Kingdom, and Saudi Arabia
778 ** have done), routine checks for iffy times may not work.
779 ** So we perform this final check, deferring it until after the time has
780 ** been set--it may take a while, and we don't want to introduce an unnecessary
781 ** lag between the time the user enters their command and the time that
782 ** stime/settimeofday is called.
784 ** We just check nearby times to see if any have the same representation
785 ** as the time that convert returned. We work our way out from the center
786 ** for quick response in solar time situations. We only handle common cases--
787 ** offsets of at most a minute, and offsets of exact numbers of minutes
788 ** and at most an hour.
790 for (offset
= 1; offset
<= 60; ++offset
)
791 for (pass
= 1; pass
<= 4; ++pass
) {
797 othert
= t
+ 60 * offset
;
798 else othert
= t
- 60 * offset
;
799 othertm
= *localtime(&othert
);
800 if (comptm(&tm
, &othertm
) == 0)
801 iffy(t
, othert
, value
,
802 _("multiple matching times exist"));
808 iffy(const time_t thist
, const time_t thatt
,
809 const char * const value
, const char * const reason
)
810 #else /* !__STDC__ */
811 iffy(thist
, thatt
, value
, reason
)
814 const char * const value
;
815 const char * const reason
;
816 #endif /* !__STDC__ */
820 (void) fprintf(stderr
, _("date: warning: ambiguous time \"%s\", %s.\n"),
822 tm
= *gmtime(&thist
);
824 ** Avoid running afoul of SCCS!
826 timeout(stderr
, _("Time was set as if you used\n\tdate -u %m%d%H\
829 tm
= *localtime(&thist
);
830 timeout(stderr
, _("to get %c"), &tm
);
831 (void) fprintf(stderr
, _(" (%s). Use\n"),
832 tm
.tm_isdst
? _("summer time") : _("standard time"));
833 tm
= *gmtime(&thatt
);
834 timeout(stderr
, _("\tdate -u %m%d%H\
837 tm
= *localtime(&thatt
);
838 timeout(stderr
, _("to get %c"), &tm
);
839 (void) fprintf(stderr
, _(" (%s).\n"),
840 tm
.tm_isdst
? _("summer time") : _("standard time"));
846 #define WAITACK 2 /* seconds */
847 #define WAITDATEACK 5 /* seconds */
850 * Set the date in the machines controlled by timedaemons
851 * by communicating the new date to the local timedaemon.
852 * If the timedaemon is in the master state, it performs the
853 * correction on all slaves. If it is in the slave state, it
854 * notifies the master that a correction is needed.
855 * Returns 1 on success, 0 on failure.
860 int s
, length
, port
, timed_ack
, found
, err
;
863 char hostname
[MAXHOSTNAMELEN
];
867 struct sockaddr_in sin
, dest
, from
;
869 sp
= getservbyname("timed", "udp");
871 fputs(_("udp/timed: unknown service\n"), stderr
);
875 dest
.sin_port
= sp
->s_port
;
876 dest
.sin_family
= AF_INET
;
877 dest
.sin_addr
.s_addr
= htonl((u_long
)INADDR_ANY
);
878 s
= socket(AF_INET
, SOCK_DGRAM
, 0);
880 if (errno
!= EPROTONOSUPPORT
)
881 perror("date: socket");
884 bzero((char *)&sin
, sizeof (sin
));
885 sin
.sin_family
= AF_INET
;
886 for (port
= IPPORT_RESERVED
- 1; port
> IPPORT_RESERVED
/ 2; port
--) {
887 sin
.sin_port
= htons((u_short
)port
);
888 if (bind(s
, (struct sockaddr
*)&sin
, sizeof (sin
)) >= 0)
890 if (errno
!= EADDRINUSE
) {
891 if (errno
!= EADDRNOTAVAIL
)
892 perror("date: bind");
896 if (port
== IPPORT_RESERVED
/ 2) {
897 fputs(_("date: All ports in use\n"), stderr
);
900 msg
.tsp_type
= TSP_SETDATE
;
901 msg
.tsp_vers
= TSPVERSION
;
902 if (gethostname(hostname
, sizeof (hostname
))) {
903 perror("gethostname");
906 (void) strncpy(msg
.tsp_name
, hostname
, sizeof (hostname
));
907 msg
.tsp_seq
= htons((u_short
)0);
908 msg
.tsp_time
.tv_sec
= htonl((u_long
)ntv
.tv_sec
);
909 msg
.tsp_time
.tv_usec
= htonl((u_long
)ntv
.tv_usec
);
910 length
= sizeof (struct sockaddr_in
);
911 if (connect(s
, &dest
, length
) < 0) {
912 perror("date: connect");
915 if (send(s
, (char *)&msg
, sizeof (struct tsp
), 0) < 0) {
916 if (errno
!= ECONNREFUSED
)
917 perror("date: send");
923 tout
.tv_sec
= waittime
;
927 found
= select(FD_SETSIZE
, &ready
, (fd_set
*)0, (fd_set
*)0, &tout
);
929 if (getsockopt(s
, SOL_SOCKET
, SO_ERROR
, (char *)&err
, &length
) == 0
932 if (errno
!= ECONNREFUSED
)
933 perror(_("date: send (delayed error)"));
936 if (found
> 0 && FD_ISSET(s
, &ready
)) {
937 length
= sizeof (struct sockaddr_in
);
938 if (recvfrom(s
, (char *)&msg
, sizeof (struct tsp
), 0, &from
,
940 if (errno
!= ECONNREFUSED
)
941 perror("date: recvfrom");
944 msg
.tsp_seq
= ntohs(msg
.tsp_seq
);
945 msg
.tsp_time
.tv_sec
= ntohl(msg
.tsp_time
.tv_sec
);
946 msg
.tsp_time
.tv_usec
= ntohl(msg
.tsp_time
.tv_usec
);
947 switch (msg
.tsp_type
) {
951 waittime
= WAITDATEACK
;
960 _("date: Wrong ack received from timed: %s\n"),
961 tsptype
[msg
.tsp_type
]);
967 fputs(_("date: Can't reach time daemon, time set locally.\n"),
974 #endif /* defined TSP_SETDATE */