adjkerntz.c revision 3368
133180Sjdp/* 233180Sjdp * Copyright (C) 1993 by Andrew A. Chernov, Moscow, Russia. 333180Sjdp * All rights reserved. 433180Sjdp * 533180Sjdp * Redistribution and use in source and binary forms, with or without 633180Sjdp * modification, are permitted provided that the following conditions 733180Sjdp * are met: 833180Sjdp * 1. Redistributions of source code must retain the above copyright 933180Sjdp * notice, this list of conditions and the following disclaimer. 1033180Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1133180Sjdp * notice, this list of conditions and the following disclaimer in the 1233180Sjdp * documentation and/or other materials provided with the distribution. 1333180Sjdp * 1433180Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 1533180Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1633180Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1733180Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1833180Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1933180Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2033180Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2133180Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2233180Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2333180Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2433180Sjdp * SUCH DAMAGE. 2533180Sjdp */ 2633180Sjdp 2790039Sobrien#ifndef lint 2890039Sobrienchar copyright[] = 2990039Sobrien"@(#)Copyright (C) 1993 by Andrew A. Chernov, Moscow, Russia.\n\ 3033180Sjdp All rights reserved.\n"; 31103436Speter#endif /* not lint */ 3233180Sjdp 3334196Sjdp/* 34168314Skan * Andrew A. Chernov <ache@astral.msk.su> Dec 20 1993 3534196Sjdp * 3634196Sjdp * Fix kernel time value if machine run wall CMOS clock 37205606Sgahr * (and /etc/wall_cmos_clock file present) 3834196Sjdp * using zoneinfo rules or direct TZ environment variable set. 3934196Sjdp * Use Joerg Wunsch idea for seconds accurate offset calculation 4034196Sjdp * with Garrett Wollman and Bruce Evans fixes. 4134196Sjdp * 4234196Sjdp */ 4334196Sjdp#include <stdio.h> 4434196Sjdp#include <stdlib.h> 4534196Sjdp#include <unistd.h> 4634196Sjdp#include <sys/stat.h> 4734196Sjdp#include <sys/time.h> 4834196Sjdp#include <sys/param.h> 4934196Sjdp#include <machine/cpu.h> 5034196Sjdp#include <sys/sysctl.h> 5134196Sjdp 5234196Sjdp#include "pathnames.h" 5334196Sjdp 5434196Sjdpint main(argc, argv) 5534196Sjdp int argc; 5634196Sjdp char **argv; 5734196Sjdp{ 5834196Sjdp struct tm local, utc; 5934196Sjdp struct timeval tv, *stv; 6034196Sjdp struct timezone tz, *stz; 6134196Sjdp int kern_offset; 6234196Sjdp size_t len; 6334196Sjdp int mib[2]; 6434196Sjdp /* Avoid time_t here, can be unsigned long or worse */ 6534196Sjdp long offset, utcsec, localsec, diff; 6634196Sjdp time_t initial_sec, final_sec; 6734196Sjdp int ch, init = -1, verbose = 0; 6834196Sjdp 6934196Sjdp while ((ch = getopt(argc, argv, "aiv")) != EOF) 7034196Sjdp switch((char)ch) { 7134196Sjdp case 'i': /* initial call, save offset */ 72205606Sgahr if (init != -1) 7334196Sjdp goto usage; 7434196Sjdp init = 1; 7534196Sjdp break; 7634196Sjdp case 'a': /* adjustment call, use saved offset */ 7734196Sjdp if (init != -1) 7855122Sjdp goto usage; 7955122Sjdp init = 0; 8055122Sjdp break; 8155122Sjdp case 'v': /* verbose */ 8255122Sjdp verbose = 1; 8355122Sjdp break; 8455122Sjdp default: 8555122Sjdp usage: 8655122Sjdp fprintf(stderr, "Usage:\n\ 8755122Sjdp\tadjkerntz -i [-v]\t(initial call from /etc/rc)\n\ 8855122Sjdp\tadjkerntz -a [-v]\t(adjustment call from crontab)\n"); 8955122Sjdp return 2; 9055122Sjdp } 9155122Sjdp if (init == -1) 9234196Sjdp goto usage; 9334196Sjdp 9434196Sjdp if (access(_PATH_CLOCK, F_OK)) 9534196Sjdp return 0; 9634196Sjdp 9734196Sjdp /* Restore saved offset */ 9834196Sjdp 9934196Sjdp mib[0] = CTL_MACHDEP; 10034196Sjdp mib[1] = CPU_ADJKERNTZ; 10134196Sjdp len = sizeof(kern_offset); 102103213Smike if (sysctl(mib, 2, &kern_offset, &len, NULL, 0) == -1) { 10334196Sjdp perror("sysctl(get_offset)"); 10434196Sjdp return 1; 10534196Sjdp } 10634196Sjdp 107110804Skan/****** Critical section, do all things as fast as possible ******/ 108190673Skib 109190673Skib /* get local CMOS clock and possible kernel offset */ 110190673Skib if (gettimeofday(&tv, &tz)) { 111190673Skib perror("gettimeofday"); 112190673Skib return 1; 113190673Skib } 114190673Skib 115190673Skib /* get the actual local timezone difference */ 116153515Skan initial_sec = tv.tv_sec; 117153515Skan local = *localtime(&initial_sec); 118153515Skan utc = *gmtime(&initial_sec); 119153515Skan utc.tm_isdst = local.tm_isdst; /* Use current timezone for mktime(), */ 120153515Skan /* because it assumed local time */ 121153515Skan 122153515Skan /* calculate local CMOS diff from GMT */ 123153515Skan 124153515Skan utcsec = mktime(&utc); 125110804Skan localsec = mktime(&local); 126110804Skan if (utcsec == -1 || localsec == -1) { 127110804Skan /* 128110804Skan * XXX user can only control local time, and it is 129110804Skan * unacceptable to fail here for -i. 2:30 am in the 130126643Smarkm * middle of the nonexistent hour means 3:30 am. 131110804Skan */ 132115401Skan fprintf(stderr, 133115401Skan "Nonexistent local time - try again in an hour\n"); 134115401Skan return 1; 135115401Skan } 136115401Skan offset = utcsec - localsec; 137115401Skan 138115401Skan /* correct the kerneltime for this diffs */ 139168314Skan /* subtract kernel offset, if present, old offset too */ 140168314Skan 141168314Skan diff = offset - tz.tz_minuteswest * 60 - kern_offset; 142168314Skan 143168314Skan if (diff != 0) { 144168314Skan 145168314Skan /* Yet one step for final time */ 146168314Skan 147168314Skan final_sec = tv.tv_sec + diff; 148185369Skib 149185369Skib /* get the actual local timezone difference */ 150185369Skib local = *localtime(&final_sec); 151185369Skib utc = *gmtime(&final_sec); 152185369Skib utc.tm_isdst = local.tm_isdst; /* Use current timezone for mktime(), */ 153185369Skib /* because it assumed local time */ 154185369Skib 155185369Skib utcsec = mktime(&utc); 156185369Skib localsec = mktime(&local); 157185369Skib if (utcsec == -1 || localsec == -1) { 158185369Skib /* 159185369Skib * XXX as above. The user has even less control, 160 * but perhaps we never get here. 161 */ 162 fprintf(stderr, 163 "Nonexistent (final) local time - try again in an hour\n"); 164 return 1; 165 } 166 offset = utcsec - localsec; 167 168 /* correct the kerneltime for this diffs */ 169 /* subtract kernel offset, if present, old offset too */ 170 171 diff = offset - tz.tz_minuteswest * 60 - kern_offset; 172 173 if (diff != 0) { 174 tv.tv_sec += diff; 175 tv.tv_usec = 0; /* we are restarting here... */ 176 stv = &tv; 177 } 178 else 179 stv = NULL; 180 } 181 else 182 stv = NULL; 183 184 if (kern_offset != offset) { 185 kern_offset = offset; 186 mib[0] = CTL_MACHDEP; 187 mib[1] = CPU_ADJKERNTZ; 188 len = sizeof(kern_offset); 189 if (sysctl(mib, 2, NULL, NULL, &kern_offset, len) == -1) { 190 perror("sysctl(update_offset)"); 191 return 1; 192 } 193 } 194 195 if (tz.tz_dsttime != 0 || tz.tz_minuteswest != 0) { 196 tz.tz_dsttime = tz.tz_minuteswest = 0; /* zone info is garbage */ 197 stz = &tz; 198 } 199 else 200 stz = NULL; 201 202 if (stz != NULL || stv != NULL) { 203 int disrtcset, need_restore = 0; 204 205 if (init && stv != NULL) { 206 mib[0] = CTL_MACHDEP; 207 mib[1] = CPU_DISRTCSET; 208 len = sizeof(disrtcset); 209 if (sysctl(mib, 2, &disrtcset, &len, NULL, 0) == -1) { 210 perror("sysctl(get_disrtcset)"); 211 return 1; 212 } 213 if (disrtcset == 0) { 214 disrtcset = 1; 215 need_restore = 1; 216 if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) { 217 perror("sysctl(set_disrtcset)"); 218 return 1; 219 } 220 } 221 } 222 if (settimeofday(stv, stz)) { 223 perror("settimeofday"); 224 return 1; 225 } 226 if (need_restore) { 227 disrtcset = 0; 228 if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) { 229 perror("sysctl(restore_disrtcset)"); 230 return 1; 231 } 232 } 233 } 234 235/****** End of critical section ******/ 236 237 if (verbose) 238 printf("Calculated zone offset difference: %ld seconds\n", 239 diff); 240 241 return 0; 242} 243 244