]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - hwclock/hwclock.c
4 * clock.c was written by Charles Hedrick, hedrick@cs.rutgers.edu, Apr 1992
5 * Modified for clock adjustments - Rob Hooft <hooft@chem.ruu.nl>, Nov 1992
6 * Improvements by Harald Koenig <koenig@nova.tat.physik.uni-tuebingen.de>
7 * and Alan Modra <alan@spri.levels.unisa.edu.au>.
9 * Major rewrite by Bryan Henderson <bryanh@giraffe-data.com>, 96.09.19.
10 * The new program is called hwclock. New features:
11 * - You can set the hardware clock without also modifying the system clock.
12 * - You can read and set the clock with finer than 1 second precision.
13 * - When you set the clock, hwclock automatically refigures the drift
14 * rate, based on how far off the clock was before you set it.
16 * Reshuffled things, added sparc code, and re-added alpha stuff
17 * by David Mosberger <davidm@azstarnet.com>
18 * and Jay Estabrook <jestabro@amt.tay1.dec.com>
19 * and Martin Ostermann <ost@coments.rwth-aachen.de>, aeb@cwi.nl, 990212.
21 * Fix for Award 2094 bug, Dave Coffin (dcoffin@shore.net) 11/12/98
22 * Change of local time handling, Stefan Ring <e9725446@stud3.tuwien.ac.at>
23 * Change of adjtime handling, James P. Rutledge <ao112@rgfn.epcc.edu>.
25 * Distributed under GPL
29 * clock [-u] -r - read hardware clock
30 * clock [-u] -w - write hardware clock from system time
31 * clock [-u] -s - set system time from hardware clock
32 * clock [-u] -a - set system time from hardware clock, adjust the time
33 * to correct for systematic error, and write it back to
35 * -u indicates cmos clock is kept in universal time
36 * -A indicates cmos clock is kept in Alpha ARC console time (0 == 1980)
37 * -J indicates we're dealing with a Jensen (early DEC Alpha PC)
41 * Explanation of `adjusting' (Rob Hooft):
43 * The problem with my machine is that its CMOS clock is 10 seconds
44 * per day slow. With this version of clock.c, and my '/etc/rc.local'
45 * reading '/etc/clock -au' instead of '/etc/clock -u -s', this error
46 * is automatically corrected at every boot.
48 * To do this job, the program reads and writes the file '/etc/adjtime'
49 * to determine the correction, and to save its data. In this file are
52 * 1) the correction in seconds per day. (So if your clock runs 5
53 * seconds per day fast, the first number should read -5.0)
54 * 2) the number of seconds since 1/1/1970 the last time the program
56 * 3) the remaining part of a second which was leftover after the last
59 * Installation and use of this program:
61 * a) create a file '/etc/adjtime' containing as the first and only line:
63 * b) run 'clock -au' or 'clock -a', depending on whether your cmos is in
64 * universal or local time. This updates the second number.
65 * c) set your system time using the 'date' command.
66 * d) update your cmos time using 'clock -wu' or 'clock -w'
67 * e) replace the first number in /etc/adjtime by your correction.
68 * f) put the command 'clock -au' or 'clock -a' in your '/etc/rc.local'
74 #include <sys/ioctl.h>
88 #define MYNAME "hwclock"
90 char *progname
= MYNAME
;
92 /* The struct that holds our hardware access routines */
95 #define FLOOR(arg) ((arg >= 0 ? (int) arg : ((int) arg) - 1));
97 /* Here the information for time adjustments is kept. */
98 #define ADJPATH "/etc/adjtime"
100 /* Store the date here when "badyear" flag is set. */
101 #define LASTDATE "/var/lib/lastdate"
104 /* This is information we keep in the adjtime file that tells us how
105 to do drift corrections. Elements are all straight from the
106 adjtime file, so see documentation of that file for details.
107 Exception is <dirty>, which is an indication that what's in this
108 structure is not what's in the disk file (because it has been
109 updated since read from the disk file).
115 time_t last_adj_time
;
119 time_t last_calib_time
;
120 /* The most recent time that we set the clock from an external
121 authority (as opposed to just doing a drift adjustment) */
124 enum a_local_utc
{LOCAL
, UTC
, UNKNOWN
} local_utc
;
125 /* To which time zone, local or UTC, we most recently set the
130 /* We are running in debug mode, wherein we put a lot of information about
131 what we're doing to standard output. */
134 /* Workaround for Award 4.50g BIOS bug: keep the year in a file. */
136 int epoch_option
= -1;
137 /* User-specified epoch, used when rtc fails to return epoch. */
140 * Almost all Award BIOS's made between 04/26/94 and 05/31/95
141 * have a nasty bug limiting the RTC year byte to the range 94-99.
142 * Any year between 2000 and 2093 gets changed to 2094, every time
143 * you start the system.
144 * With the --badyear option, we write the date to file and hope
145 * that the file is updated at least once a year.
146 * I recommend putting this command "hwclock --badyear" in the monthly
147 * crontab, just to be safe. -- Dave Coffin 11/12/98
150 write_date_to_file (struct tm
*tm
) {
153 if ((fp
= fopen(LASTDATE
,"w"))) {
154 fprintf(fp
,"%02d.%02d.%04d\n", tm
->tm_mday
, tm
->tm_mon
+1,
162 read_date_from_file (struct tm
*tm
) {
163 int last_mday
, last_mon
, last_year
;
166 if ((fp
= fopen(LASTDATE
,"r"))) {
167 if (fscanf (fp
,"%d.%d.%d\n", &last_mday
, &last_mon
, &last_year
) == 3) {
168 tm
->tm_year
= last_year
-1900;
169 if ((tm
->tm_mon
<< 5) + tm
->tm_mday
< ((last_mon
-1) << 5) + last_mday
)
174 write_date_to_file (tm
);
178 time_diff(struct timeval subtrahend
, struct timeval subtractor
) {
179 /*---------------------------------------------------------------------------
180 The difference in seconds between two times in "timeval" format.
181 ----------------------------------------------------------------------------*/
182 return (subtrahend
.tv_sec
- subtractor
.tv_sec
)
183 + (subtrahend
.tv_usec
- subtractor
.tv_usec
) / 1E6
;
187 static struct timeval
188 time_inc(struct timeval addend
, double increment
) {
189 /*----------------------------------------------------------------------------
190 The time, in "timeval" format, which is <increment> seconds after
191 the time <addend>. Of course, <increment> may be negative.
192 -----------------------------------------------------------------------------*/
193 struct timeval newtime
;
195 newtime
.tv_sec
= addend
.tv_sec
+ (int) increment
;
196 newtime
.tv_usec
= addend
.tv_usec
+ (increment
- (int) increment
) * 1E6
;
198 /* Now adjust it so that the microsecond value is between 0 and 1 million */
199 if (newtime
.tv_usec
< 0) {
200 newtime
.tv_usec
+= 1E6
;
202 } else if (newtime
.tv_usec
>= 1E6
) {
203 newtime
.tv_usec
-= 1E6
;
211 hw_clock_is_utc(const bool utc
, const bool local_opt
,
212 const struct adjtime adjtime
) {
216 ret
= TRUE
; /* --utc explicitly given on command line */
218 ret
= FALSE
; /* --localtime explicitly given */
220 /* get info from adjtime file - default is local */
221 ret
= (adjtime
.local_utc
== UTC
);
223 printf(_("Assuming hardware clock is kept in %s time.\n"),
224 ret
? _("UTC") : _("local"));
231 read_adjtime(struct adjtime
*adjtime_p
) {
232 /*----------------------------------------------------------------------------
233 Read the adjustment parameters out of the /etc/adjtime file.
235 Return them as the adjtime structure <*adjtime_p>.
236 If there is no /etc/adjtime file, return defaults.
237 If values are missing from the file, return defaults for them.
239 return value 0 if all OK, !=0 otherwise.
241 -----------------------------------------------------------------------------*/
243 int rc
; /* local return code */
244 struct stat statbuf
; /* We don't even use the contents of this. */
246 rc
= stat(ADJPATH
, &statbuf
);
247 if (rc
< 0 && errno
== ENOENT
) {
248 /* He doesn't have a adjtime file, so we'll use defaults. */
249 adjtime_p
->drift_factor
= 0;
250 adjtime_p
->last_adj_time
= 0;
251 adjtime_p
->not_adjusted
= 0;
252 adjtime_p
->last_calib_time
= 0;
253 adjtime_p
->local_utc
= UNKNOWN
;
258 adjfile
= fopen(ADJPATH
, "r"); /* open file for reading */
259 if (adjfile
== NULL
) {
260 outsyserr("cannot open file " ADJPATH
);
265 char line1
[81]; /* String: first line of adjtime file */
266 char line2
[81]; /* String: second line of adjtime file */
267 char line3
[81]; /* String: third line of adjtime file */
270 line1
[0] = '\0'; /* In case fgets fails */
271 fgets(line1
, sizeof(line1
), adjfile
);
272 line2
[0] = '\0'; /* In case fgets fails */
273 fgets(line2
, sizeof(line2
), adjfile
);
274 line3
[0] = '\0'; /* In case fgets fails */
275 fgets(line3
, sizeof(line3
), adjfile
);
279 /* Set defaults in case values are missing from file */
280 adjtime_p
->drift_factor
= 0;
281 adjtime_p
->last_adj_time
= 0;
282 adjtime_p
->not_adjusted
= 0;
283 adjtime_p
->last_calib_time
= 0;
286 sscanf(line1
, "%lf %ld %lf",
287 &adjtime_p
->drift_factor
,
289 &adjtime_p
->not_adjusted
);
290 adjtime_p
->last_adj_time
= timeval
;
292 sscanf(line2
, "%ld", &timeval
);
293 adjtime_p
->last_calib_time
= timeval
;
295 if (!strcmp(line3
, "UTC\n"))
296 adjtime_p
->local_utc
= UTC
;
297 else if (!strcmp(line3
, "LOCAL\n"))
298 adjtime_p
->local_utc
= LOCAL
;
300 adjtime_p
->local_utc
= UNKNOWN
;
303 _("%s: Warning: unrecognized third line in adjtime file\n"),
305 fprintf(stderr
, _("(Expected: `UTC' or `LOCAL' or nothing.)\n"));
309 adjtime_p
->dirty
= FALSE
;
312 printf(_("Last drift adjustment done at %ld seconds after 1969\n"),
313 (long) adjtime_p
->last_adj_time
);
314 printf(_("Last calibration done at %ld seconds after 1969\n"),
315 (long) adjtime_p
->last_calib_time
);
316 printf(_("Hardware clock is on %s time\n"),
317 (adjtime_p
->local_utc
== LOCAL
) ? _("local") :
318 (adjtime_p
->local_utc
== UTC
) ? _("UTC") : _("unknown"));
326 synchronize_to_clock_tick(void) {
327 /*-----------------------------------------------------------------------------
328 Wait until the falling edge of the Hardware Clock's update flag so
329 that any time that is read from the clock immediately after we
330 return will be exact.
332 The clock only has 1 second precision, so it gives the exact time only
333 once per second, right on the falling edge of the update flag.
335 We wait (up to one second) either blocked waiting for an rtc device
336 or in a CPU spin loop. The former is probably not very accurate.
338 Return 0 if it worked, nonzero if it didn't.
339 -----------------------------------------------------------------------------*/
342 if (debug
) printf(_("Waiting for clock tick...\n"));
344 rc
= ur
->synchronize_to_clock_tick();
346 if (debug
) printf(_("...got clock tick\n"));
354 mktime_tz(struct tm tm
, const bool universal
,
355 bool *valid_p
, time_t *systime_p
) {
356 /*-----------------------------------------------------------------------------
357 Convert a time in broken down format (hours, minutes, etc.) into standard
358 unix time (seconds into epoch). Return it as *systime_p.
360 The broken down time is argument <tm>. This broken down time is either in
361 local time zone or UTC, depending on value of logical argument "universal".
362 True means it is in UTC.
364 If the argument contains values that do not constitute a valid time,
365 and mktime() recognizes this, return *valid_p == false and
366 *systime_p undefined. However, mktime() sometimes goes ahead and
367 computes a fictional time "as if" the input values were valid,
368 e.g. if they indicate the 31st day of April, mktime() may compute
369 the time of May 1. In such a case, we return the same fictional
370 value mktime() does as *systime_p and return *valid_p == true.
372 -----------------------------------------------------------------------------*/
373 time_t mktime_result
; /* The value returned by our mktime() call */
374 char *zone
; /* Local time zone name */
376 /* We use the C library function mktime(), but since it only works on
377 local time zone input, we may have to fake it out by temporarily
378 changing the local time zone to UTC.
380 zone
= getenv("TZ"); /* remember original time zone */
382 /* Set timezone to UTC */
383 setenv("TZ", "", TRUE
);
384 /* Note: tzset() gets called implicitly by the time code, but only the
385 first time. When changing the environment variable, better call
390 mktime_result
= mktime(&tm
);
391 if (mktime_result
== -1) {
392 /* This apparently (not specified in mktime() documentation) means
393 the 'tm' structure does not contain valid values (however, not
394 containing valid values does _not_ imply mktime() returns -1).
399 printf(_("Invalid values in hardware clock: "
400 "%4d/%.2d/%.2d %.2d:%.2d:%.2d\n"),
401 tm
.tm_year
+1900, tm
.tm_mon
+1, tm
.tm_mday
,
402 tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
405 *systime_p
= mktime_result
;
407 printf(_("Hw clock time : %4d/%.2d/%.2d %.2d:%.2d:%.2d = "
408 "%ld seconds since 1969\n"),
409 tm
.tm_year
+1900, tm
.tm_mon
+1, tm
.tm_mday
,
410 tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, (long) *systime_p
);
412 /* now put back the original zone. */
413 if (zone
) setenv("TZ", zone
, TRUE
);
420 read_hardware_clock(const bool universal
, bool *valid_p
, time_t *systime_p
){
421 /*----------------------------------------------------------------------------
422 Read the hardware clock and return the current time via <tm> argument.
424 Use the method indicated by <method> argument to access the hardware clock.
425 -----------------------------------------------------------------------------*/
429 err
= ur
->read_hardware_clock(&tm
);
432 read_date_from_file(&tm
);
435 printf (_("Time read from Hardware Clock: %4d/%.2d/%.2d %02d:%02d:%02d\n"),
436 tm
.tm_year
+1900, tm
.tm_mon
+1, tm
.tm_mday
,
437 tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
438 mktime_tz(tm
, universal
, valid_p
, systime_p
);
443 set_hardware_clock(const time_t newtime
,
444 const bool universal
,
445 const bool testing
) {
446 /*----------------------------------------------------------------------------
447 Set the Hardware Clock to the time <newtime>, in local time zone or UTC,
448 according to <universal>.
449 ----------------------------------------------------------------------------*/
451 struct tm new_broken_time
;
452 /* Time to which we will set Hardware Clock, in broken down format, in
453 the time zone of caller's choice
457 new_broken_time
= *gmtime(&newtime
);
459 new_broken_time
= *localtime(&newtime
);
462 printf(_("Setting Hardware Clock to %.2d:%.2d:%.2d "
463 "= %ld seconds since 1969\n"),
464 new_broken_time
.tm_hour
, new_broken_time
.tm_min
,
465 new_broken_time
.tm_sec
, (long) newtime
);
468 printf(_("Clock not changed - testing only.\n"));
472 * Write the real year to a file, then write a fake year
473 * between 1995 and 1998 to the RTC. This way, Award BIOS boots
474 * on 29 Feb 2000 thinking that it's 29 Feb 1996.
476 write_date_to_file (&new_broken_time
);
477 new_broken_time
.tm_year
= 95 + ((new_broken_time
.tm_year
+1) & 3);
479 err
= ur
->set_hardware_clock(&new_broken_time
);
486 set_hardware_clock_exact(const time_t settime
,
487 const struct timeval ref_time
,
488 const bool universal
,
489 const bool testing
) {
490 /*----------------------------------------------------------------------------
491 Set the Hardware Clock to the time "settime", in local time zone or UTC,
492 according to "universal".
494 But correct "settime" and wait for a fraction of a second so that
495 "settime" is the value of the Hardware Clock as of system time
496 "ref_time", which is in the past. For example, if "settime" is
497 14:03:05 and "ref_time" is 12:10:04.5 and the current system
498 time is 12:10:06.0: Wait .5 seconds (to make exactly 2 seconds since
499 "ref_time") and then set the Hardware Clock to 14:03:07, thus
500 getting a precise and retroactive setting of the clock.
502 (Don't be confused by the fact that the system clock and the Hardware
503 Clock differ by two hours in the above example. That's just to remind
504 you that there are two independent time scales here).
506 This function ought to be able to accept set times as fractional times.
507 Idea for future enhancement.
509 -----------------------------------------------------------------------------*/
510 time_t newtime
; /* Time to which we will set Hardware Clock */
511 struct timeval now_time
; /* locally used time */
513 gettimeofday(&now_time
, NULL
);
514 newtime
= settime
+ (int) time_diff(now_time
, ref_time
) + 1;
516 printf(_("Time elapsed since reference time has been %.6f seconds.\n"
517 "Delaying further to reach the next full second.\n"),
518 time_diff(now_time
, ref_time
));
520 /* Now delay some more until Hardware Clock time newtime arrives */
521 do gettimeofday(&now_time
, NULL
);
522 while (time_diff(now_time
, ref_time
) < newtime
- settime
);
524 set_hardware_clock(newtime
, universal
, testing
);
530 display_time(const bool hclock_valid
, const time_t systime
,
531 const double sync_duration
) {
532 /*----------------------------------------------------------------------------
533 Put the time "systime" on standard output in display format.
534 Except if hclock_valid == false, just tell standard output that we don't
535 know what time it is.
537 Include in the output the adjustment "sync_duration".
538 -----------------------------------------------------------------------------*/
540 fprintf(stderr
, _("The Hardware Clock registers contain values that are "
541 "either invalid (e.g. 50th day of month) or beyond the range "
542 "we can handle (e.g. Year 2095).\n"));
548 lt
= localtime(&systime
);
549 strftime(ctime_now
, sizeof(ctime_now
), format
, lt
);
550 printf(_("%s %.6f seconds\n"), ctime_now
, -(sync_duration
));
557 interpret_date_string(const char *date_opt
, time_t * const time_p
) {
558 /*----------------------------------------------------------------------------
559 Interpret the value of the --date option, which is something like
560 "13:05:01". In fact, it can be any of the myriad ASCII strings that specify
561 a time which the "date" program can understand. The date option value in
562 question is our "dateopt" argument.
564 The specified time is in the local time zone.
566 Our output, "*time_p", is a seconds-into-epoch time.
568 We use the "date" program to interpret the date string. "date" must be
569 runnable by issuing the command "date" to the /bin/sh shell. That means
570 in must be in the current PATH.
572 If anything goes wrong (and many things can), we return return code
573 10 and arbitrary *time_p. Otherwise, return code is 0 and *time_p
575 ----------------------------------------------------------------------------*/
578 const char magic
[]="seconds-into-epoch=";
579 char date_command
[100];
580 int retcode
; /* our eventual return code */
581 int rc
; /* local return code */
583 if (date_opt
== NULL
) {
584 fprintf(stderr
, _("No --date option specified.\n"));
588 /* prevent overflow - a security risk */
589 if (strlen(date_opt
) > sizeof(date_command
) - 50) {
590 fprintf(stderr
, _("--date argument too long\n"));
594 /* Quotes in date_opt would ruin the date command we construct. */
595 if (strchr(date_opt
, '"') != NULL
) {
597 _("The value of the --date option is not a valid date.\n"
598 "In particular, it contains quotation marks.\n"));
602 sprintf(date_command
, "date --date=\"%s\" +seconds-into-epoch=%%s",
605 printf(_("Issuing date command: %s\n"), date_command
);
607 date_child_fp
= popen(date_command
, "r");
608 if (date_child_fp
== NULL
) {
609 outsyserr(_("Unable to run 'date' program in /bin/sh shell. "
614 date_resp
[0] = '\0'; /* in case fgets fails */
615 fgets(date_resp
, sizeof(date_resp
), date_child_fp
);
617 printf(_("response from date command = %s\n"), date_resp
);
618 if (strncmp(date_resp
, magic
, sizeof(magic
)-1) != 0) {
619 fprintf(stderr
, _("The date command issued by %s returned "
620 "unexpected results.\n"
621 "The command was:\n %s\n"
622 "The response was:\n %s\n"),
623 MYNAME
, date_command
, date_resp
);
626 long seconds_since_epoch
;
627 rc
= sscanf(date_resp
+ sizeof(magic
)-1, "%ld",
628 &seconds_since_epoch
);
631 _("The date command issued by %s returned "
632 "something other than an integer where the "
633 "converted time value was expected.\n"
634 "The command was:\n %s\n"
635 "The response was:\n %s\n"),
636 MYNAME
, date_command
, date_resp
);
640 *time_p
= seconds_since_epoch
;
642 printf(_("date string %s equates to "
643 "%ld seconds since 1969.\n"),
644 date_opt
, (long) *time_p
);
647 fclose(date_child_fp
);
655 set_system_clock(const bool hclock_valid
, const time_t newtime
,
656 const bool testing
) {
657 /*----------------------------------------------------------------------------
658 Set the System Clock to time 'newtime'.
660 Also set the kernel time zone value to the value indicated by the
661 TZ environment variable and/or /usr/lib/zoneinfo/, interpreted as
662 tzset() would interpret them.
664 EXCEPT: if hclock_valid is false, just issue an error message
665 saying there is no valid time in the Hardware Clock to which to set
668 If 'testing' is true, don't actually update anything -- just say we
670 -----------------------------------------------------------------------------*/
674 fprintf(stderr
, _("The Hardware Clock does not contain a valid time, so "
675 "we cannot set the System Time from it.\n"));
686 broken
= localtime(&newtime
);
687 #ifdef HAVE_tm_gmtoff
688 minuteswest
= -broken
->tm_gmtoff
/60; /* GNU extension */
690 minuteswest
= timezone
/60;
691 if (broken
->tm_isdst
)
696 printf(_("Calling settimeofday:\n"));
697 printf(_("\ttv.tv_sec = %ld, tv.tv_usec = %ld\n"),
698 (long) tv
.tv_sec
, (long) tv
.tv_usec
);
699 printf(_("\ttz.tz_minuteswest = %d\n"), minuteswest
);
702 printf(_("Not setting system clock because running in test mode.\n"));
705 const struct timezone tz
= { minuteswest
, 0 };
707 rc
= settimeofday(&tv
, &tz
);
709 if (errno
== EPERM
) {
711 _("Must be superuser to set system clock.\n"));
714 outsyserr(_("settimeofday() failed"));
725 adjust_drift_factor(struct adjtime
*adjtime_p
,
726 const time_t nowtime
,
727 const bool hclock_valid
,
728 const time_t hclocktime
,
729 const double sync_delay
) {
730 /*------------------------------------------------------------------------
731 Update the drift factor in <*adjtime_p> to reflect the fact that the
732 Hardware Clock was calibrated to <nowtime> and before that was set
735 We record in the adjtime file the time at which we last calibrated
736 the clock so we can compute the drift rate each time we calibrate.
738 EXCEPT: if <hclock_valid> is false, assume Hardware Clock was not set
739 before to anything meaningful and regular adjustments have not been
740 done, so don't adjust the drift factor.
741 ------------------------------------------------------------------------*/
744 printf(_("Not adjusting drift factor because the "
745 "Hardware Clock previously contained "
747 } else if (adjtime_p
->last_calib_time
== 0) {
749 printf(_("Not adjusting drift factor because last "
750 "calibration time is zero,\n"
751 "so history is bad and calibration startover "
753 } else if ((hclocktime
- adjtime_p
->last_calib_time
) < 23 * 60 * 60) {
755 printf(_("Not adjusting drift factor because it has "
756 "been less than a day since the last "
758 } else if (adjtime_p
->last_calib_time
!= 0) {
760 * At adjustment time we adjust the hardware clock according
761 * to the contents of /etc/adjtime.
763 * At calibration time we set the hardware clock and
764 * update /etc/adjtime, that is, for each calibration
765 * (except the first) we also do an adjustment.
767 * We are now at calibration time.
769 * Let us do computation in doubles. (Floats almost suffice,
770 * but 195 days + 1 second equals 195 days in floats.)
772 const double sec_per_day
= 24.0 * 60.0 * 60.0;
773 double atime_per_htime
;
774 double adj_days
, cal_days
;
775 double exp_drift
, unc_drift
;
776 double factor_adjust
;
778 /* Adjusted time units per hardware time unit */
779 atime_per_htime
= 1.0 + adjtime_p
->drift_factor
/ sec_per_day
;
781 /* Days since last adjustment (in hardware clock time) */
782 adj_days
= (double)(hclocktime
- adjtime_p
->last_adj_time
)
785 /* Expected drift (sec) since last adjustment */
786 exp_drift
= adj_days
* adjtime_p
->drift_factor
787 + adjtime_p
->not_adjusted
;
789 /* Uncorrected drift (sec) since last calibration */
790 unc_drift
= (double)(nowtime
- hclocktime
)
791 + sync_delay
- exp_drift
;
793 /* Days since last calibration (in hardware clock time) */
794 cal_days
= ((double)(adjtime_p
->last_adj_time
795 - adjtime_p
->last_calib_time
)
796 + adjtime_p
->not_adjusted
)
797 / (sec_per_day
* atime_per_htime
) + adj_days
;
799 /* Amount to add to previous drift factor */
800 factor_adjust
= unc_drift
/ cal_days
;
803 printf(_("Clock drifted %.1f seconds in the past "
804 "%d seconds in spite of a drift factor of "
806 "Adjusting drift factor by %f seconds/day\n"),
808 (int) (nowtime
- adjtime_p
->last_calib_time
),
809 adjtime_p
->drift_factor
,
812 adjtime_p
->drift_factor
+= factor_adjust
;
814 adjtime_p
->last_calib_time
= nowtime
;
816 adjtime_p
->last_adj_time
= nowtime
;
818 adjtime_p
->not_adjusted
= 0;
820 adjtime_p
->dirty
= TRUE
;
826 calculate_adjustment(const double factor
,
827 const time_t last_time
,
828 const double not_adjusted
,
829 const time_t systime
,
833 /*----------------------------------------------------------------------------
834 Do the drift adjustment calculation.
836 The way we have to set the clock, we need the adjustment in two parts:
838 1) an integer number of seconds (return as *adjustment_p)
840 2) a positive fraction of a second (less than 1) (return as *retro_p)
842 The sum of these two values is the adjustment needed. Positive means to
843 advance the clock or insert seconds. Negative means to retard the clock
845 ----------------------------------------------------------------------------*/
846 double exact_adjustment
;
848 exact_adjustment
= ((double) (systime
- last_time
)) * factor
/ (24 * 60 * 60)
850 *adjustment_p
= FLOOR(exact_adjustment
);
852 *retro_p
= exact_adjustment
- (double) *adjustment_p
;
854 printf (_("Time since last adjustment is %d seconds\n"),
855 (int) (systime
- last_time
));
856 printf (_("Need to insert %d seconds and refer time back "
857 "%.6f seconds ago\n"),
858 *adjustment_p
, *retro_p
);
865 save_adjtime(const struct adjtime adjtime
, const bool testing
) {
866 /*-----------------------------------------------------------------------------
867 Write the contents of the <adjtime> structure to its disk file.
869 But if the contents are clean (unchanged since read from disk), don't
871 -----------------------------------------------------------------------------*/
872 char newfile
[412]; /* Stuff to write to disk file */
875 /* snprintf is not always available, but this is safe
876 as long as libc does not use more than 100 positions for %ld or %f */
877 sprintf(newfile
, "%f %ld %f\n%ld\n%s\n",
878 adjtime
.drift_factor
,
879 (long) adjtime
.last_adj_time
,
880 adjtime
.not_adjusted
,
881 (long) adjtime
.last_calib_time
,
882 (adjtime
.local_utc
== UTC
) ? "UTC" : "LOCAL");
885 printf(_("Not updating adjtime file because of testing mode.\n"));
886 printf(_("Would have written the following to %s:\n%s"),
892 adjfile
= fopen(ADJPATH
, "w");
893 if (adjfile
== NULL
) {
894 outsyserr("Could not open file with the clock adjustment parameters "
895 "in it (" ADJPATH
") for writing");
898 if (fputs(newfile
, adjfile
) < 0) {
899 outsyserr("Could not update file with the clock adjustment "
900 "parameters (" ADJPATH
") in it");
903 if (fclose(adjfile
) < 0) {
904 outsyserr("Could not update file with the clock adjustment "
905 "parameters (" ADJPATH
") in it");
910 fprintf(stderr
, _("Drift adjustment parameters not updated.\n"));
918 do_adjustment(struct adjtime
*adjtime_p
,
919 const bool hclock_valid
, const time_t hclocktime
,
920 const struct timeval read_time
,
921 const bool universal
, const bool testing
) {
922 /*---------------------------------------------------------------------------
923 Do the adjustment requested, by 1) setting the Hardware Clock (if
924 necessary), and 2) updating the last-adjusted time in the adjtime
927 Do not update anything if the Hardware Clock does not currently present
930 arguments <factor> and <last_time> are current values from the adjtime
933 <hclock_valid> means the Hardware Clock contains a valid time, and that
934 time is <hclocktime>.
936 <read_time> is the current system time (to be precise, it is the system
937 time at the time <hclocktime> was read, which due to computational delay
938 could be a short time ago).
940 <universal>: the Hardware Clock is kept in UTC.
942 <testing>: We are running in test mode (no updating of clock).
944 We do not bother to update the clock if the adjustment would be less than
945 one second. This is to avoid cumulative error and needless CPU hogging
946 (remember we use an infinite loop for some timing) if the user runs us
949 ----------------------------------------------------------------------------*/
951 fprintf(stderr
, _("The Hardware Clock does not contain a valid time, "
952 "so we cannot adjust it.\n"));
953 adjtime_p
->last_calib_time
= 0; /* calibration startover is required */
954 adjtime_p
->last_adj_time
= 0;
955 adjtime_p
->not_adjusted
= 0;
956 adjtime_p
->dirty
= TRUE
;
957 } else if (adjtime_p
->last_adj_time
== 0) {
959 printf("Not setting clock because last adjustment time is zero, "
960 "so history is bad.");
963 /* Number of seconds we must insert in the Hardware Clock */
965 /* Fraction of second we have to remove from clock after inserting
966 <adjustment> whole seconds.
968 calculate_adjustment(adjtime_p
->drift_factor
,
969 adjtime_p
->last_adj_time
,
970 adjtime_p
->not_adjusted
,
974 if (adjustment
> 0 || adjustment
< -1) {
975 set_hardware_clock_exact(hclocktime
+ adjustment
,
976 time_inc(read_time
, -retro
),
978 adjtime_p
->last_adj_time
= hclocktime
+ adjustment
;
979 adjtime_p
->not_adjusted
= 0;
980 adjtime_p
->dirty
= TRUE
;
983 printf(_("Needed adjustment is less than one second, "
984 "so not setting clock.\n"));
991 determine_clock_access_method(const bool user_requests_ISA
) {
995 if (user_requests_ISA
)
996 ur
= probe_for_cmos_clock();
999 ur
= probe_for_rtc_clock();
1002 ur
= probe_for_kd_clock();
1004 if (!ur
&& !user_requests_ISA
)
1005 ur
= probe_for_cmos_clock();
1009 printf(_("Using %s.\n"), ur
->interface_name
);
1011 printf(_("No usable clock interface found.\n"));
1016 manipulate_clock(const bool show
, const bool adjust
, const bool noadjfile
,
1017 const bool set
, const time_t set_time
,
1018 const bool hctosys
, const bool systohc
,
1019 const struct timeval startup_time
,
1020 const bool utc
, const bool local_opt
,
1021 const bool testing
) {
1022 /*---------------------------------------------------------------------------
1023 Do all the normal work of hwclock - read, set clock, etc.
1025 Issue output to stdout and error message to stderr where appropriate.
1027 Return rc == 0 if everything went OK, rc != 0 if not.
1028 ----------------------------------------------------------------------------*/
1029 struct adjtime adjtime
;
1030 /* Contents of the adjtime file, or what they should be. */
1031 int rc
; /* local return code */
1032 bool no_auth
; /* User lacks necessary authorization to access the clock */
1034 no_auth
= ur
->get_permissions();
1038 if (!noadjfile
&& (adjust
|| set
|| systohc
|| (!utc
&& !local_opt
))) {
1039 rc
= read_adjtime(&adjtime
);
1043 /* A little trick to avoid reading the file if we don't have to */
1044 adjtime
.dirty
= FALSE
;
1049 const bool universal
= hw_clock_is_utc(utc
, local_opt
, adjtime
);
1051 if ((set
|| systohc
|| adjust
) &&
1052 (adjtime
.local_utc
== UTC
) != universal
) {
1053 adjtime
.local_utc
= universal
? UTC
: LOCAL
;
1054 adjtime
.dirty
= TRUE
;
1057 rc
= synchronize_to_clock_tick(); /* this takes up to 1 second */
1062 struct timeval read_time
;
1063 /* The time at which we read the Hardware Clock */
1066 /* The Hardware Clock gives us a valid time, or at least something
1067 close enough to fool mktime().
1071 /* The time the hardware clock had just after we
1072 synchronized to its next clock tick when we started up.
1073 Defined only if hclock_valid is true.
1076 gettimeofday(&read_time
, NULL
);
1077 read_hardware_clock(universal
, &hclock_valid
, &hclocktime
);
1080 display_time(hclock_valid
, hclocktime
,
1081 time_diff(read_time
, startup_time
));
1083 set_hardware_clock_exact(set_time
, startup_time
,
1084 universal
, testing
);
1085 adjust_drift_factor(&adjtime
, set_time
, hclock_valid
, hclocktime
,
1086 time_diff(read_time
, startup_time
));
1087 } else if (adjust
) {
1088 do_adjustment(&adjtime
, hclock_valid
, hclocktime
,
1089 read_time
, universal
, testing
);
1090 } else if (systohc
) {
1091 struct timeval nowtime
, reftime
;
1092 /* We can only set_hardware_clock_exact to a whole seconds
1093 time, so we set it with reference to the most recent
1096 gettimeofday(&nowtime
, NULL
);
1097 reftime
.tv_sec
= nowtime
.tv_sec
;
1098 reftime
.tv_usec
= 0;
1100 set_hardware_clock_exact((time_t) reftime
.tv_sec
, reftime
,
1101 universal
, testing
);
1102 adjust_drift_factor(&adjtime
, (time_t) reftime
.tv_sec
, hclock_valid
,
1103 hclocktime
, (double) read_time
.tv_usec
/ 1E6
);
1104 } else if (hctosys
) {
1105 rc
= set_system_clock(hclock_valid
, hclocktime
, testing
);
1107 printf(_("Unable to set system clock.\n"));
1112 save_adjtime(adjtime
, testing
);
1120 manipulate_epoch(const bool getepoch
, const bool setepoch
,
1121 const int epoch_opt
, const bool testing
) {
1122 /*----------------------------------------------------------------------------
1123 Get or set the Hardware Clock epoch value in the kernel, as appropriate.
1124 <getepoch>, <setepoch>, and <epoch> are hwclock invocation options.
1126 <epoch> == -1 if the user did not specify an "epoch" option.
1128 -----------------------------------------------------------------------------*/
1130 Maintenance note: This should work on non-Alpha machines, but the
1131 evidence today (98.03.04) indicates that the kernel only keeps the
1132 epoch value on Alphas. If that is ever fixed, this function should be
1137 fprintf(stderr
, _("The kernel keeps an epoch value for the Hardware Clock "
1138 "only on an Alpha machine.\nThis copy of hwclock was built for "
1139 "a machine other than Alpha\n(and thus is presumably not running "
1140 "on an Alpha now). No action taken.\n"));
1143 unsigned long epoch
;
1145 if (get_epoch_rtc(&epoch
, 0))
1146 fprintf(stderr
, _("Unable to get the epoch value from the kernel.\n"));
1148 printf(_("Kernel is assuming an epoch value of %lu\n"), epoch
);
1149 } else if (setepoch
) {
1150 if (epoch_opt
== -1)
1151 fprintf(stderr
, _("To set the epoch value, you must use the 'epoch' "
1152 "option to tell to what value to set it.\n"));
1154 printf(_("Not setting the epoch to %d - testing only.\n"),
1156 else if (set_epoch_rtc(epoch_opt
))
1157 printf(_("Unable to set the epoch value in the kernel.\n"));
1163 #define RTC_DEV "/dev/efirtc"
1165 #define RTC_DEV "/dev/rtc"
1170 printf(_("%s from %s\n"), MYNAME
, util_linux_version
);
1174 usage - Output (error and) usage information
1176 This function is called both directly from main to show usage
1177 information and as fatal function from shhopt if some argument is
1178 not understood. In case of normal usage info FMT should be NULL.
1179 In that case the info is printed to stdout. If FMT is given
1180 usage will act like fprintf( stderr, fmt, ... ), show a usage
1181 information and terminate the program afterwards.
1184 usage( const char *fmt
, ... ) {
1188 usageto
= fmt
? stderr
: stdout
;
1190 fprintf( usageto
, _(
1191 "hwclock - query and set the hardware clock (RTC)\n\n"
1192 "Usage: hwclock [function] [options...]\n\n"
1194 " --help show this help\n"
1195 " --show read hardware clock and print result\n"
1196 " --set set the rtc to the time given with --date\n"
1197 " --hctosys set the system time from the hardware clock\n"
1198 " --systohc set the hardware clock to the current system time\n"
1199 " --adjust adjust the rtc to account for systematic drift since \n"
1200 " the clock was last set or adjusted\n"
1201 " --getepoch print out the kernel's hardware clock epoch value\n"
1202 " --setepoch set the kernel's hardware clock epoch value to the \n"
1203 " value given with --epoch\n"
1204 " --version print out the version of hwclock to stdout\n"
1206 " --utc the hardware clock is kept in coordinated universal time\n"
1207 " --localtime the hardware clock is kept in local time\n"
1208 " --directisa access the ISA bus directly instead of %s\n"
1209 " --badyear ignore rtc's year because the bios is broken\n"
1210 " --date specifies the time to which to set the hardware clock\n"
1211 " --epoch=year specifies the year which is the beginning of the \n"
1212 " hardware clock's epoch value\n"
1213 " --noadjfile do not access /etc/adjtime. Requires the use of\n"
1214 " either --utc or --localtime\n"
1218 " --jensen, --arc, --srm, --funky-toy\n"
1219 " tell hwclock the type of alpha you have (see hwclock(8))\n"
1228 vfprintf(stderr
, fmt
, ap
);
1232 exit(fmt
? EX_USAGE
: 0);
1235 static const struct option longopts
[] = {
1236 { "adjust", 0, 0, 'a' },
1237 { "help", 0, 0, 'h' },
1238 { "show", 0, 0, 'r' },
1239 { "hctosys", 0, 0, 's' },
1240 { "utc", 0, 0, 'u' },
1241 { "version", 0, 0, 'v' },
1242 { "systohc", 0, 0, 'w' },
1243 { "debug", 0, 0, 'D' },
1245 { "ARC", 0, 0, 'A' },
1246 { "arc", 0, 0, 'A' },
1247 { "Jensen", 0, 0, 'J' },
1248 { "jensen", 0, 0, 'J' },
1249 { "SRM", 0, 0, 'S' },
1250 { "srm", 0, 0, 'S' },
1251 { "funky-toy", 0, 0, 'F'}
1253 { "set", 0, 0, 128 },
1254 { "getepoch", 0, 0, 129 },
1255 { "setepoch", 0, 0, 130 },
1256 { "noadjfile", 0, 0, 131 },
1257 { "localtime", 0, 0, 132 },
1258 { "badyear", 0, 0, 133 },
1259 { "directisa", 0, 0, 134 },
1260 { "test", 0, 0, 135 },
1261 { "date", 1, 0, 136 },
1262 { "epoch", 1, 0, 137 },
1268 * EX_USAGE: bad invocation
1269 * EX_NOPERM: no permission
1270 * EX_OSFILE: cannot open /dev/rtc or /etc/adjtime
1271 * EX_IOERR: ioctl error getting or setting the time
1276 main(int argc
, char **argv
) {
1278 struct timeval startup_time
;
1279 /* The time we started up, in seconds into the epoch, including
1281 time_t set_time
; /* Time to which user said to set Hardware Clock */
1283 bool permitted
; /* User is permitted to do the function */
1286 /* Variables set by various options; show may also be set later */
1287 /* The options debug, badyear and epoch_option are global */
1288 bool show
, set
, systohc
, hctosys
, adjust
, getepoch
, setepoch
;
1289 bool utc
, testing
, local_opt
, noadjfile
, directisa
;
1290 bool ARCconsole
, Jensen
, SRM
, funky_toy
;
1293 /* Remember what time we were invoked */
1294 gettimeofday(&startup_time
, NULL
);
1296 setlocale(LC_ALL
, "");
1298 /* We need LC_CTYPE and LC_TIME and LC_MESSAGES, but must avoid
1299 LC_NUMERIC since it gives problems when we write to /etc/adjtime.
1300 - gqueri@mail.dotcom.fr */
1301 setlocale(LC_NUMERIC
, "C");
1303 bindtextdomain(PACKAGE
, LOCALEDIR
);
1304 textdomain(PACKAGE
);
1306 /* Set option defaults */
1307 show
= set
= systohc
= hctosys
= adjust
= noadjfile
= FALSE
;
1308 getepoch
= setepoch
= utc
= local_opt
= testing
= debug
= FALSE
;
1309 ARCconsole
= Jensen
= SRM
= funky_toy
= directisa
= badyear
= FALSE
;
1312 while ((c
= getopt_long (argc
, argv
, "?hvVDarsuwAJSF", longopts
, NULL
))
1360 local_opt
= TRUE
; /* --localtime */
1369 testing
= TRUE
; /* --test */
1372 date_opt
= optarg
; /* --date */
1375 epoch_option
= atoi(optarg
); /* --epoch */
1377 case 'v': /* --version */
1381 case 'h': /* --help */
1392 usage(_("%s takes no non-option arguments. "
1393 "You supplied %d.\n"),
1397 if (show
+ set
+ systohc
+ hctosys
+ adjust
+ getepoch
+ setepoch
> 1){
1398 fprintf(stderr
, _("You have specified multiple functions.\n"
1399 "You can only perform one function "
1404 if (utc
&& local_opt
) {
1405 fprintf(stderr
, _("%s: The --utc and --localtime options "
1406 "are mutually exclusive. You specified "
1407 "both.\n"), MYNAME
);
1411 if (adjust
&& noadjfile
) {
1412 fprintf(stderr
, _("%s: The --adjust and --noadjfile options "
1413 "are mutually exclusive. You specified "
1414 "both.\n"), MYNAME
);
1418 if (noadjfile
&& !(utc
|| local_opt
)) {
1419 fprintf(stderr
, _("%s: With --noadjfile, you must specify "
1420 "either --utc or --localtime\n"), MYNAME
);
1425 set_cmos_epoch(ARCconsole
, SRM
);
1426 set_cmos_access(Jensen
, funky_toy
);
1430 rc
= interpret_date_string(date_opt
, &set_time
);
1431 /* (time-consuming) */
1433 fprintf(stderr
, _("No usable set-to time. "
1434 "Cannot set clock.\n"));
1439 if (!(show
| set
| systohc
| hctosys
| adjust
| getepoch
| setepoch
))
1440 show
= 1; /* default to show */
1446 /* program is designed to run setuid (in some situations) */
1447 if (set
|| hctosys
|| systohc
|| adjust
) {
1449 _("Sorry, only the superuser can change "
1450 "the Hardware Clock.\n"));
1452 } else if (hctosys
) {
1454 _("Sorry, only the superuser can change "
1455 "the System Clock.\n"));
1457 } else if (setepoch
) {
1459 _("Sorry, only the superuser can change the "
1460 "Hardware Clock epoch in the kernel.\n"));
1469 if (getepoch
|| setepoch
) {
1470 manipulate_epoch(getepoch
, setepoch
, epoch_option
, testing
);
1476 determine_clock_access_method(directisa
);
1479 _("Cannot access the Hardware Clock via "
1480 "any known method.\n"));
1483 _("Use the --debug option to see the details "
1484 "of our search for an access method.\n"));
1488 return manipulate_clock(show
, adjust
, noadjfile
, set
, set_time
,
1489 hctosys
, systohc
, startup_time
, utc
,
1490 local_opt
, testing
);
1493 /* A single routine for greater uniformity */
1495 outsyserr(char *msg
, ...) {
1499 fprintf(stderr
, "%s: ", progname
);
1500 va_start(args
, msg
);
1501 vfprintf(stderr
, msg
, args
);
1503 fprintf(stderr
, ", errno=%d: %s.\n",
1504 errsv
, strerror(errsv
));
1507 /****************************************************************************
1509 History of this program:
1511 98.08.12 BJH Version 2.4
1513 Don't use century byte from Hardware Clock. Add comments telling why.
1516 98.06.20 BJH Version 2.3.
1518 Make --hctosys set the kernel timezone from TZ environment variable
1519 and/or /usr/lib/zoneinfo. From Klaus Ripke (klaus@ripke.com).
1521 98.03.05 BJH. Version 2.2.
1523 Add --getepoch and --setepoch.
1525 Fix some word length things so it works on Alpha.
1527 Make it work when /dev/rtc doesn't have the interrupt functions.
1528 In this case, busywait for the top of a second instead of blocking and
1529 waiting for the update complete interrupt.
1531 Fix a bunch of bugs too numerous to mention.
1533 97.06.01: BJH. Version 2.1. Read and write the century byte (Byte
1534 50) of the ISA Hardware Clock when using direct ISA I/O. Problem
1535 discovered by job (jei@iclnl.icl.nl).
1537 Use the rtc clock access method in preference to the KDGHWCLK method.
1538 Problem discovered by Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>.
1540 November 1996: Version 2.0.1. Modifications by Nicolai Langfeldt
1541 (janl@math.uio.no) to make it compile on linux 1.2 machines as well
1542 as more recent versions of the kernel. Introduced the NO_CLOCK
1543 access method and wrote feature test code to detect absense of rtc
1547 **************************************************************************
1550 To compile this, you must use GNU compiler optimization (-O option)
1551 in order to make the "extern inline" functions from asm/io.h (inb(),
1552 etc.) compile. If you don't optimize, which means the compiler
1553 will generate no inline functions, the references to these functions
1554 in this program will be compiled as external references. Since you
1555 probably won't be linking with any functions by these names, you will
1556 have unresolved external references when you link.
1558 The program is designed to run setuid superuser, since we need to be
1559 able to do direct I/O. (More to the point: we need permission to
1560 execute the iopl() system call). (However, if you use one of the
1561 methods other than direct ISA I/O to access the clock, no setuid is
1564 Here's some info on how we must deal with the time that elapses while
1565 this program runs: There are two major delays as we run:
1567 1) Waiting up to 1 second for a transition of the Hardware Clock so
1568 we are synchronized to the Hardware Clock.
1570 2) Running the "date" program to interpret the value of our --date
1573 Reading the /etc/adjtime file is the next biggest source of delay and
1576 The user wants to know what time it was at the moment he invoked us,
1577 not some arbitrary time later. And in setting the clock, he is
1578 giving us the time at the moment we are invoked, so if we set the
1579 clock some time later, we have to add some time to that.
1581 So we check the system time as soon as we start up, then run "date"
1582 and do file I/O if necessary, then wait to synchronize with a
1583 Hardware Clock edge, then check the system time again to see how
1584 much time we spent. We immediately read the clock then and (if
1585 appropriate) report that time, and additionally, the delay we measured.
1587 If we're setting the clock to a time given by the user, we wait some
1588 more so that the total delay is an integral number of seconds, then
1589 set the Hardware Clock to the time the user requested plus that
1590 integral number of seconds. N.B. The Hardware Clock can only be set
1591 in integral seconds.
1593 If we're setting the clock to the system clock value, we wait for
1594 the system clock to reach the top of a second, and then set the
1595 Hardware Clock to the system clock's value.
1597 Here's an interesting point about setting the Hardware Clock: On my
1598 machine, when you set it, it sets to that precise time. But one can
1599 imagine another clock whose update oscillator marches on a steady one
1600 second period, so updating the clock between any two oscillator ticks
1601 is the same as updating it right at the earlier tick. To avoid any
1602 complications that might cause, we set the clock as soon as possible
1603 after an oscillator tick.
1606 About synchronizing to the Hardware Clock when reading the time: The
1607 precision of the Hardware Clock counters themselves is one second.
1608 You can't read the counters and find out that is 12:01:02.5. But if
1609 you consider the location in time of the counter's ticks as part of
1610 its value, then its precision is as infinite as time is continuous!
1611 What I'm saying is this: To find out the _exact_ time in the
1612 hardware clock, we wait until the next clock tick (the next time the
1613 second counter changes) and measure how long we had to wait. We
1614 then read the value of the clock counters and subtract the wait time
1615 and we know precisely what time it was when we set out to query the
1618 hwclock uses this method, and considers the Hardware Clock to have
1622 Enhancements needed:
1624 - When waiting for whole second boundary in set_hardware_clock_exact,
1625 fail if we miss the goal by more than .1 second, as could happen if
1626 we get pre-empted (by the kernel dispatcher).
1628 ****************************************************************************/