diff options
Diffstat (limited to 'sbin/adjkerntz/adjkerntz.c')
-rw-r--r-- | sbin/adjkerntz/adjkerntz.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/sbin/adjkerntz/adjkerntz.c b/sbin/adjkerntz/adjkerntz.c new file mode 100644 index 000000000000..8cb885cc52b6 --- /dev/null +++ b/sbin/adjkerntz/adjkerntz.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 1993 by Andrew A. Chernov, Moscow, Russia. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#)Copyright (C) 1993 by Andrew A. Chernov, Moscow, Russia.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +/* + * Andrew A. Chernov <ache@astral.msk.su> Dec 15 1993 + * + * Fix kernel time value if machine run wall CMOS clock + * (and /etc/wall_cmos_clock file present) + * using zoneinfo rules or direct TZ environment variable set. + * Use Joerg Wunsch idea for seconds accurate offset calculation + * with Garrett Wollman and Bruce Evans fixes. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include "pathnames.h" + +char storage[] = _PATH_OFFSET; + +int main(argc, argv) + int argc; + char **argv; +{ + struct tm local, utc; + struct timeval tv, *stv; + struct timezone tz, *stz; + /* Avoid time_t here, can be unsigned long */ + long offset, oldoffset, utcsec, localsec, diff; + int ch, init = -1, verbose = 0; + FILE *f; + + while ((ch = getopt(argc, argv, "aiv")) != EOF) + switch((char)ch) { + case 'i': /* initial call, save offset */ + if (init != -1) + goto usage; + init = 1; + break; + case 'a': /* adjustment call, use saved offset */ + if (init != -1) + goto usage; + init = 0; + break; + case 'v': /* verbose */ + verbose = 1; + break; + default: + usage: + fprintf(stderr, "Usage:\n\ +\tadjkerntz -i [-v]\t(initial call from /etc/rc)\n\ +\tadjkerntz -a [-v]\t(adjustment call from crontab)\n"); + return 2; + } + if (init == -1) + goto usage; + + if (access(_PATH_CLOCK, F_OK)) + return 0; + + /* Restore saved offset */ + + if (!init) { + if ((f = fopen(storage, "r")) == NULL) { + perror(storage); + return 1; + } + if (fscanf(f, "%ld", &oldoffset) != 1) { + fprintf(stderr, "Incorrect offset in %s\n", storage); + return 1; + } + (void) fclose(f); + } + else + oldoffset = 0; + +/****** Critical section, do all things as fast as possible ******/ + + /* get local CMOS clock and possible kernel offset */ + if (gettimeofday(&tv, &tz)) { + perror("gettimeofday"); + return 1; + } + + /* get the actual local timezone difference */ + local = *localtime(&tv.tv_sec); + utc = *gmtime(&tv.tv_sec); + utc.tm_isdst = local.tm_isdst; /* Use current timezone for mktime(), */ + /* because it assumed local time */ + + /* calculate local CMOS diff from GMT */ + + utcsec = mktime(&utc); + localsec = mktime(&local); + if (utcsec == -1 || localsec == -1) { + fprintf(stderr, "Wrong hour to call\n"); + return 1; + } + offset = utcsec - localsec; + + /* correct the kerneltime for this diffs */ + /* subtract kernel offset, if present, old offset too */ + + diff = oldoffset + tz.tz_minuteswest * 60 - offset; + if (diff != 0) { + tv.tv_sec += diff; + tv.tv_usec = 0; /* we are restarting here... */ + stv = &tv; + } + else + stv = NULL; + + if (tz.tz_dsttime != 0 || tz.tz_minuteswest != 0) { + tz.tz_dsttime = tz.tz_minuteswest = 0; /* zone info is garbage */ + stz = &tz; + } + else + stz = NULL; + + if (stz != NULL || stv != NULL) { + if (settimeofday(stv, stz)) { + perror("settimeofday"); + return 1; + } + } + +/****** End of critical section ******/ + + if (verbose) + printf("Calculated zone offset diffs: %ld seconds\n", diff); + + if (offset != oldoffset) { + (void) umask(022); + /* Save offset for next calls from crontab */ + if ((f = fopen(storage, "w")) == NULL) { + perror(storage); + return 1; + } + fprintf(f, "%ld\n", offset); + if (fclose(f)) { + perror(storage); + return 1; + } + } + + return 0; +} + |