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.
27 #include <fcntl.h> /* for O_WRONLY, O_APPEND */
31 "@(#) Copyright (c) 1985, 1987, 1988 The Regents of the University of California.\n\
32 All rights reserved.\n";
36 static char sccsid
[] = "@(#)date.c 4.23 (Berkeley) 9/20/88";
40 #if HAVE_ADJTIME || HAVE_SETTIMEOFDAY
41 #include "sys/time.h" /* for struct timeval, struct timezone */
42 #endif /* HAVE_ADJTIME || HAVE_SETTIMEOFDAY */
44 #include "utmp.h" /* for OLD_TIME (or its absence) */
50 #define OTIME_MSG "old time"
53 #define NTIME_MSG "new time"
57 ** The two things date knows about time are. . .
61 #define TM_YEAR_BASE 1900
62 #endif /* !defined TM_YEAR_BASE */
66 #endif /* !defined SECSPERMIN */
68 extern char ** environ
;
71 extern char * getlogin();
72 extern time_t mktime();
75 extern char * strchr();
77 extern char * tzname
[2];
80 static int retval
= EXIT_SUCCESS
;
82 static void checkfinal
P((const char *, int, time_t, time_t));
83 static int comptm
P((const struct tm
*, const struct tm
*));
84 static time_t convert
P((const char *, int, time_t));
85 static void display
P((const char *));
86 static void dogmt
P((void));
87 static void errensure
P((void));
88 static void iffy
P((time_t, time_t, const char *, const char *));
89 int main
P((int, char**));
90 static const char * nondigit
P((const char *));
91 static void oops
P((const char *));
92 static void reset
P((time_t, int));
93 static void timeout
P((FILE *, const char *, const struct tm
*));
94 static void usage
P((void));
95 static void wildinput
P((const char *, const char *,
103 register const char * format
;
104 register const char * value
;
105 register const char * cp
;
108 register int aflag
= 0;
109 register int dflag
= 0;
110 register int nflag
= 0;
111 register int tflag
= 0;
112 register int minuteswest
;
113 register int dsttime
;
114 register double adjust
;
119 INITIALIZE(minuteswest
);
124 (void) setlocale(LC_ALL
, "");
125 #endif /* defined(LC_ALL) */
128 (void) bindtextdomain(TZ_DOMAIN
, TZ_DOMAINDIR
);
129 #endif /* defined(TEXTDOMAINDIR) */
130 (void) textdomain(TZ_DOMAIN
);
131 #endif /* HAVE_GETTEXT */
133 format
= value
= NULL
;
134 while ((ch
= getopt(argc
, argv
, "ucnd:t:a:")) != EOF
&& ch
!= -1) {
138 case 'u': /* do it in UTC */
142 case 'n': /* don't set network */
145 case 'd': /* daylight saving time */
147 (void) fprintf(stderr
,
148 _("date: error: multiple -d's used"));
154 if (*cp
== '\0' || *nondigit(cp
) != '\0')
155 wildinput(_("-t value"), optarg
,
156 _("must be a non-negative number"));
158 case 't': /* minutes west of UTC */
160 (void) fprintf(stderr
,
161 _("date: error: multiple -t's used"));
166 minuteswest
= atoi(cp
);
167 if (*cp
== '+' || *cp
== '-')
169 if (*cp
== '\0' || *nondigit(cp
) != '\0')
170 wildinput(_("-d value"), optarg
,
171 _("must be a number"));
173 case 'a': /* adjustment */
175 (void) fprintf(stderr
,
176 _("date: error: multiple -a's used"));
182 if (*cp
== '+' || *cp
== '-')
184 if (*cp
== '\0' || strcmp(cp
, ".") == 0)
185 wildinput(_("-a value"), optarg
,
186 _("must be a number"));
190 if (*nondigit(cp
) != '\0')
191 wildinput(_("-a value"), optarg
,
192 _("must be a number"));
196 while (optind
< argc
) {
202 (void) fprintf(stderr
,
203 _("date: error: multiple formats in command line\n"));
206 else if (value
== NULL
)
209 (void) fprintf(stderr
,
210 _("date: error: multiple values in command line\n"));
216 ** This order ensures that "reasonable" twelve-digit inputs
217 ** (such as 120203042006) won't be misinterpreted
218 ** even if time_t's range all the way back to the thirteenth
219 ** century. Do not change the order.
221 t
= convert(value
, (dousg
= TRUE
), now
);
223 t
= convert(value
, (dousg
= FALSE
), now
);
226 ** Out of range values,
227 ** or time that falls in a DST transition hole?
229 if ((cp
= strchr(value
, '.')) != NULL
) {
231 ** Ensure that the failure of
232 ** TZ=America/New_York date 8712312359.60
233 ** doesn't get misdiagnosed. (It was
234 ** TZ=America/New_York date 8712311859.60
235 ** when the leap second was inserted.)
236 ** The normal check won't work since
237 ** the given time is valid in UTC.
239 if (atoi(cp
+ 1) >= SECSPERMIN
)
240 wildinput(_("time"), value
,
241 _("out of range seconds given"));
244 t
= convert(value
, FALSE
, now
);
246 t
= convert(value
, TRUE
, now
);
247 wildinput(_("time"), value
,
249 _("out of range value given") :
250 _("time skipped when clock springs forward"));
254 ** Entire command line has now been checked.
260 tv
.tv_sec
= (int) adjust
;
261 tv
.tv_usec
= (int) ((adjust
- tv
.tv_sec
) * 1000000L);
262 if (adjtime(&tv
, (struct timeval
*) NULL
) != 0)
264 #endif /* HAVE_ADJTIME */
266 reset((time_t) (now
+ adjust
), nflag
);
267 #endif /* !HAVE_ADJTIME */
269 ** Sun silently ignores everything else; we follow suit.
273 if (dflag
|| tflag
) {
274 #if HAVE_SETTIMEOFDAY == 2
277 if (!dflag
|| !tflag
)
278 if (gettimeofday((struct timeval
*) NULL
, &tz
) != 0)
279 oops("gettimeofday");
281 tz
.tz_dsttime
= dsttime
;
283 tz
.tz_minuteswest
= minuteswest
;
284 if (settimeofday((struct timeval
*) NULL
, &tz
) != 0)
285 oops("settimeofday");
286 #endif /* HAVE_SETTIMEOFDAY == 2 */
287 #if HAVE_SETTIMEOFDAY != 2
288 (void) fprintf(stderr
,
289 _("date: warning: kernel doesn't keep -d/-t information, option ignored\n"));
290 #endif /* HAVE_SETTIMEOFDAY != 2 */
298 checkfinal(value
, dousg
, t
, now
);
305 timeout(stdout
, "%c\n", &tm
);
308 #endif /* defined EBUG */
312 /* gcc -Wall pacifier */
320 static char ** fakeenv
;
322 if (fakeenv
== NULL
) {
326 static char tzegmt0
[] = "TZ=GMT0";
328 for (n
= 0; environ
[n
] != NULL
; ++n
)
330 fakeenv
= (char **) malloc((size_t) (n
+ 2) * sizeof *fakeenv
);
331 if (fakeenv
== NULL
) {
332 (void) perror(_("Memory exhausted"));
337 fakeenv
[to
++] = tzegmt0
;
338 for (from
= 1; environ
[from
] != NULL
; ++from
)
339 if (strncmp(environ
[from
], "TZ=", 3) != 0)
340 fakeenv
[to
++] = environ
[from
];
349 ** We assume we're on a System-V-based system,
351 ** should write System-V-format utmp entries,
352 ** and don't have network notification to worry about.
358 reset(const time_t newt
, const int nflag
)
359 #else /* !__STDC__ */
363 #endif /* !__STDC__ */
379 ** Wouldn't it be great if stime returned the old time?
382 if (stime(&newt
) != 0)
384 s
.before
.ut_type
= OLD_TIME
;
385 s
.before
.ut_time
= oldt
;
386 (void) strcpy(s
.before
.ut_line
, OTIME_MSG
);
387 s
.after
.ut_type
= NEW_TIME
;
388 s
.after
.ut_time
= newt
;
389 (void) strcpy(s
.after
.ut_line
, NTIME_MSG
);
390 fid
= open(WTMP_FILE
, O_WRONLY
| O_APPEND
);
392 oops(_("log file open"));
393 if (write(fid
, (char *) &s
, sizeof s
) != sizeof s
)
394 oops(_("log file write"));
396 oops(_("log file close"));
398 pututline(&s
.before
);
400 #endif /* !HAVE_UTMPX_H */
402 sx
.before
.ut_type
= OLD_TIME
;
403 sx
.before
.ut_tv
.tv_sec
= oldt
;
404 (void) strcpy(sx
.before
.ut_line
, OTIME_MSG
);
405 sx
.after
.ut_type
= NEW_TIME
;
406 sx
.after
.ut_tv
.tv_sec
= newt
;
407 (void) strcpy(sx
.after
.ut_line
, NTIME_MSG
);
408 #if !SUPPRESS_WTMPX_FILE_UPDATE
409 /* In Solaris 2.5 (and presumably other systems),
410 `date' does not update /var/adm/wtmpx.
411 This must be a bug. If you'd like to reproduce the bug,
412 define SUPPRESS_WTMPX_FILE_UPDATE to be nonzero. */
413 fid
= open(WTMPX_FILE
, O_WRONLY
| O_APPEND
);
415 oops(_("log file open"));
416 if (write(fid
, (char *) &sx
, sizeof sx
) != sizeof sx
)
417 oops(_("log file write"));
419 oops(_("log file close"));
420 #endif /* !SUPPRESS_WTMPX_FILE_UPDATE */
421 pututxline(&sx
.before
);
422 pututxline(&sx
.after
);
423 #endif /* HAVE_UTMPX_H */
426 #endif /* defined OLD_TIME */
430 ** We assume we're on a BSD-based system,
431 ** should use settimeofday,
432 ** should write BSD-format utmp entries (using logwtmp),
433 ** and may get to worry about network notification.
434 ** The "time name" changes between 4.3-tahoe and 4.4;
435 ** we include sys/param.h to determine which we should use.
439 #include "sys/param.h"
441 #define TIME_NAME "date"
442 #endif /* defined BSD4_4 */
445 #endif /* !defined BSD4_4 */
446 #endif /* !defined TIME_NAME */
449 #include "sys/socket.h"
450 #include "netinet/in.h"
455 extern int logwtmp();
458 #if HAVE_SETTIMEOFDAY == 1
459 #define settimeofday(t, tz) (settimeofday)(t)
460 #endif /* HAVE_SETTIMEOFDAY == 1 */
464 #endif /* !defined TSP_SETDATE */
470 register const char * username
;
471 static struct timeval tv
; /* static so tv_usec is 0 */
475 #endif /* defined EBUG */
476 username
= getlogin();
477 if (username
== NULL
|| *username
== '\0') /* single-user or no tty */
481 if (nflag
|| !netsettime(tv
))
482 #endif /* defined TSP_SETDATE */
485 ** "old" entry is always written, for compatibility.
487 logwtmp("|", TIME_NAME
, "");
488 if (settimeofday(&tv
, (struct timezone
*) NULL
) == 0) {
489 logwtmp("{", TIME_NAME
, ""); /* } */
490 syslog(LOG_AUTH
| LOG_NOTICE
, _("date set by %s"),
492 } else oops("settimeofday");
496 #endif /* !defined OLD_TIME */
499 wildinput(item
, value
, reason
)
500 const char * const item
;
501 const char * const value
;
502 const char * const reason
;
504 (void) fprintf(stderr
,
505 _("date: error: bad command line %s \"%s\", %s\n"),
506 item
, value
, reason
);
513 if (retval
== EXIT_SUCCESS
)
514 retval
= EXIT_FAILURE
;
519 register const char * cp
;
521 while (is_digit(*cp
))
529 (void) fprintf(stderr
, _("date: usage is date [-u] [-c] [-n] [-d dst] \
530 [-t min-west] [-a sss.fff] [[yyyy]mmddhhmm[yyyy][.ss]] [+format]\n"));
537 const char * const string
;
541 (void) fprintf(stderr
, _("date: error: "));
543 (void) perror(string
);
545 display((char *) NULL
);
550 const char * const format
;
556 tm
= *localtime(&now
);
557 timeout(stdout
, format
? format
: "%+", &tm
);
558 (void) putchar('\n');
559 (void) fflush(stdout
);
560 (void) fflush(stderr
);
561 if (ferror(stdout
) || ferror(stderr
)) {
562 (void) fprintf(stderr
,
563 _("date: error: couldn't write results\n"));
570 extern size_t strftime();
576 timeout(fp
, format
, tmp
)
578 const char * const format
;
579 const struct tm
* const tmp
;
588 cp
= malloc((size_t) size
);
591 (void) fprintf(stderr
,
592 _("date: error: can't get memory\n"));
597 result
= strftime(cp
, size
, format
, tmp
);
598 if (result
!= 0 || cp
[0] == '\0')
601 cp
= realloc(cp
, (size_t) size
);
603 (void) fwrite(cp
, 1, result
, fp
);
609 register const struct tm
* const atmp
;
610 register const struct tm
* const btmp
;
614 if ((result
= (atmp
->tm_year
- btmp
->tm_year
)) == 0 &&
615 (result
= (atmp
->tm_mon
- btmp
->tm_mon
)) == 0 &&
616 (result
= (atmp
->tm_mday
- btmp
->tm_mday
)) == 0 &&
617 (result
= (atmp
->tm_hour
- btmp
->tm_hour
)) == 0 &&
618 (result
= (atmp
->tm_min
- btmp
->tm_min
)) == 0)
619 result
= atmp
->tm_sec
- btmp
->tm_sec
;
625 ** convert user's input into a time_t.
628 #define ATOI2(ar) (ar[0] - '0') * 10 + (ar[1] - '0'); ar += 2;
632 convert(register const char * const value
, const int dousg
, const time_t t
)
633 #else /* !__STDC__ */
634 convert(value
, dousg
, t
)
635 register const char * const value
;
638 #endif /* !__STDC__ */
640 register const char * cp
;
641 register const char * dotp
;
642 register int cent
, year_in_cent
, month
, hour
, day
, mins
, secs
;
648 year_in_cent
= tm
.tm_year
% DIVISOR
+ TM_YEAR_BASE
% DIVISOR
;
649 cent
= tm
.tm_year
/ DIVISOR
+ TM_YEAR_BASE
/ DIVISOR
+
650 year_in_cent
/ DIVISOR
;
651 year_in_cent
%= DIVISOR
;
652 if (year_in_cent
< 0) {
653 year_in_cent
+= DIVISOR
;
656 month
= tm
.tm_mon
+ 1;
662 dotp
= strchr(value
, '.');
663 for (cp
= value
; *cp
!= '\0'; ++cp
)
664 if (!is_digit(*cp
) && cp
!= dotp
)
665 wildinput(_("time"), value
, _("contains a nondigit"));
668 dotp
= strchr(value
, '\0');
672 wildinput(_("time"), value
,
673 _("seconds part is not two digits"));
680 wildinput(_("time"), value
,
681 _("main part is wrong length"));
685 year_in_cent
= ATOI2(cp
);
693 year_in_cent
= ATOI2(cp
);
696 case 8: /* mmddhhmm */
698 /* fall through to. . . */
701 /* fall through to. . . */
708 year_in_cent
= ATOI2(cp
);
715 year_in_cent
= ATOI2(cp
);
720 tm
.tm_year
= cent
* 100 + year_in_cent
- TM_YEAR_BASE
;
721 tm
.tm_mon
= month
- 1;
728 outt
= mktime(&outtm
);
729 return (comptm(&tm
, &outtm
) == 0) ? outt
: -1;
733 ** Code from here on out is either based on code provided by UCB
734 ** or is only called just before the program exits.
738 ** Check for iffy input.
743 checkfinal(const char * const value
,
747 #else /* !__STDC__ */
748 checkfinal(value
, didusg
, t
, oldnow
)
749 const char * const value
;
753 #endif /* !__STDC__ */
759 register long offset
;
762 ** See if there's both a USG and a BSD interpretation.
764 othert
= convert(value
, !didusg
, oldnow
);
765 if (othert
!= -1 && othert
!= t
)
766 iffy(t
, othert
, value
, _("year could be at start or end"));
768 ** See if there's both a DST and a STD version.
772 othertm
.tm_isdst
= !tm
.tm_isdst
;
773 othert
= mktime(&othertm
);
774 if (othert
!= -1 && othertm
.tm_isdst
!= tm
.tm_isdst
&&
775 comptm(&tm
, &othertm
) == 0)
776 iffy(t
, othert
, value
,
777 _("both standard and summer time versions exist"));
781 ** If a jurisdiction shifts time *without* shifting whether time is
782 ** summer or standard (as Hawaii, the United Kingdom, and Saudi Arabia
783 ** have done), routine checks for iffy times may not work.
784 ** So we perform this final check, deferring it until after the time has
785 ** been set--it may take a while, and we don't want to introduce an unnecessary
786 ** lag between the time the user enters their command and the time that
787 ** stime/settimeofday is called.
789 ** We just check nearby times to see if any have the same representation
790 ** as the time that convert returned. We work our way out from the center
791 ** for quick response in solar time situations. We only handle common cases--
792 ** offsets of at most a minute, and offsets of exact numbers of minutes
793 ** and at most an hour.
795 for (offset
= 1; offset
<= 60; ++offset
)
796 for (pass
= 1; pass
<= 4; ++pass
) {
802 othert
= t
+ 60 * offset
;
803 else othert
= t
- 60 * offset
;
804 othertm
= *localtime(&othert
);
805 if (comptm(&tm
, &othertm
) == 0)
806 iffy(t
, othert
, value
,
807 _("multiple matching times exist"));
813 iffy(const time_t thist
, const time_t thatt
,
814 const char * const value
, const char * const reason
)
815 #else /* !__STDC__ */
816 iffy(thist
, thatt
, value
, reason
)
819 const char * const value
;
820 const char * const reason
;
821 #endif /* !__STDC__ */
825 (void) fprintf(stderr
, _("date: warning: ambiguous time \"%s\", %s.\n"),
827 tm
= *gmtime(&thist
);
829 ** Avoid running afoul of SCCS!
831 timeout(stderr
, _("Time was set as if you used\n\tdate -u %m%d%H\
834 tm
= *localtime(&thist
);
835 timeout(stderr
, _("to get %c"), &tm
);
836 (void) fprintf(stderr
, _(" (%s). Use\n"),
837 tm
.tm_isdst
? _("summer time") : _("standard time"));
838 tm
= *gmtime(&thatt
);
839 timeout(stderr
, _("\tdate -u %m%d%H\
842 tm
= *localtime(&thatt
);
843 timeout(stderr
, _("to get %c"), &tm
);
844 (void) fprintf(stderr
, _(" (%s).\n"),
845 tm
.tm_isdst
? _("summer time") : _("standard time"));
851 #define WAITACK 2 /* seconds */
852 #define WAITDATEACK 5 /* seconds */
855 * Set the date in the machines controlled by timedaemons
856 * by communicating the new date to the local timedaemon.
857 * If the timedaemon is in the master state, it performs the
858 * correction on all slaves. If it is in the slave state, it
859 * notifies the master that a correction is needed.
860 * Returns 1 on success, 0 on failure.
865 int s
, length
, port
, timed_ack
, found
, err
;
868 char hostname
[MAXHOSTNAMELEN
];
872 struct sockaddr_in sin
, dest
, from
;
874 sp
= getservbyname("timed", "udp");
876 fputs(_("udp/timed: unknown service\n"), stderr
);
880 dest
.sin_port
= sp
->s_port
;
881 dest
.sin_family
= AF_INET
;
882 dest
.sin_addr
.s_addr
= htonl((u_long
)INADDR_ANY
);
883 s
= socket(AF_INET
, SOCK_DGRAM
, 0);
885 if (errno
!= EPROTONOSUPPORT
)
886 perror("date: socket");
889 bzero((char *)&sin
, sizeof (sin
));
890 sin
.sin_family
= AF_INET
;
891 for (port
= IPPORT_RESERVED
- 1; port
> IPPORT_RESERVED
/ 2; port
--) {
892 sin
.sin_port
= htons((u_short
)port
);
893 if (bind(s
, (struct sockaddr
*)&sin
, sizeof (sin
)) >= 0)
895 if (errno
!= EADDRINUSE
) {
896 if (errno
!= EADDRNOTAVAIL
)
897 perror("date: bind");
901 if (port
== IPPORT_RESERVED
/ 2) {
902 fputs(_("date: All ports in use\n"), stderr
);
905 msg
.tsp_type
= TSP_SETDATE
;
906 msg
.tsp_vers
= TSPVERSION
;
907 if (gethostname(hostname
, sizeof (hostname
))) {
908 perror("gethostname");
911 (void) strncpy(msg
.tsp_name
, hostname
, sizeof (hostname
));
912 msg
.tsp_seq
= htons((u_short
)0);
913 msg
.tsp_time
.tv_sec
= htonl((u_long
)ntv
.tv_sec
);
914 msg
.tsp_time
.tv_usec
= htonl((u_long
)ntv
.tv_usec
);
915 length
= sizeof (struct sockaddr_in
);
916 if (connect(s
, &dest
, length
) < 0) {
917 perror("date: connect");
920 if (send(s
, (char *)&msg
, sizeof (struct tsp
), 0) < 0) {
921 if (errno
!= ECONNREFUSED
)
922 perror("date: send");
928 tout
.tv_sec
= waittime
;
932 found
= select(FD_SETSIZE
, &ready
, (fd_set
*)0, (fd_set
*)0, &tout
);
934 if (getsockopt(s
, SOL_SOCKET
, SO_ERROR
, (char *)&err
, &length
) == 0
937 if (errno
!= ECONNREFUSED
)
938 perror(_("date: send (delayed error)"));
941 if (found
> 0 && FD_ISSET(s
, &ready
)) {
942 length
= sizeof (struct sockaddr_in
);
943 if (recvfrom(s
, (char *)&msg
, sizeof (struct tsp
), 0, &from
,
945 if (errno
!= ECONNREFUSED
)
946 perror("date: recvfrom");
949 msg
.tsp_seq
= ntohs(msg
.tsp_seq
);
950 msg
.tsp_time
.tv_sec
= ntohl(msg
.tsp_time
.tv_sec
);
951 msg
.tsp_time
.tv_usec
= ntohl(msg
.tsp_time
.tv_usec
);
952 switch (msg
.tsp_type
) {
956 waittime
= WAITDATEACK
;
965 _("date: Wrong ack received from timed: %s\n"),
966 tsptype
[msg
.tsp_type
]);
972 fputs(_("date: Can't reach time daemon, time set locally.\n"),
979 #endif /* defined TSP_SETDATE */