diff options
Diffstat (limited to 'toys')
-rw-r--r-- | toys/pending/hwclock.c | 178 |
1 files changed, 75 insertions, 103 deletions
diff --git a/toys/pending/hwclock.c b/toys/pending/hwclock.c index 003174f7..5ca035aa 100644 --- a/toys/pending/hwclock.c +++ b/toys/pending/hwclock.c @@ -2,9 +2,9 @@ * * Copyright 2014 Bilal Qureshi <bilal.jmi@gmail.com> * - * No Standard. + * No standard, but see Documentation/rtc.txt in the linux kernel source.. * -USE_HWCLOCK(NEWTOY(hwclock, ">0(fast)f(rtc):u(utc)l(localtime)t(systz)w(systohc)s(hctosys)r(show)[!ul][!rsw]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_HWCLOCK(NEWTOY(hwclock, ">0(fast)f(rtc):u(utc)l(localtime)t(systz)w(systohc)s(hctosys)r(show)[-ul][!rtsw]", TOYFLAG_USR|TOYFLAG_BIN)) config HWCLOCK bool "hwclock" @@ -12,7 +12,7 @@ config HWCLOCK help usage: hwclock [-rswtluf] - -f FILE Use specified device file instead of /dev/rtc (--show) + -f FILE Use specified device file instead of /dev/rtc (--rtc) -l Hardware clock uses localtime (--localtime) -r Show hardware clock time (--show) -s Set system time from hardware clock (--hctosys) @@ -20,6 +20,7 @@ config HWCLOCK -u Hardware clock uses UTC (--utc) -w Set hardware clock from system time (--systohc) */ + #define FOR_hwclock #include "toys.h" #include <linux/rtc.h> @@ -30,7 +31,7 @@ GLOBALS( int utc; ) -static int check_hctosys(struct dirtree* node) +static int rtc_find(struct dirtree* node) { FILE *fp; @@ -39,147 +40,118 @@ static int check_hctosys(struct dirtree* node) snprintf(toybuf, sizeof(toybuf), "/sys/class/rtc/%s/hctosys", node->name); fp = fopen(toybuf, "r"); if (fp) { - int hctosys = 0; - int items = fscanf(fp, "%d", &hctosys); + int hctosys = 0, items = fscanf(fp, "%d", &hctosys); + fclose(fp); if (items == 1 && hctosys == 1) { snprintf(toybuf, sizeof(toybuf), "/dev/%s", node->name); TT.fname = toybuf; + return DIRTREE_ABORT; } } - return 0; -} -// Search /sys/class/rtc for the RTC that the system clock is set from. -// See the kernel's Documentation/rtc.txt. -static int open_wall_clock_rtc(int flag) -{ - TT.fname = NULL; - dirtree_read("/sys/class/rtc", check_hctosys); - return TT.fname ? xopen(TT.fname, flag) : -1; + return 0; } +// Your system should have a /dev/rtc symlink. If your system is misconfigured, +// search /sys/class/rtc for RTC system clock set from, then try /dev/misc/rtc. static int rtc_open(int flag) { if (!TT.fname) { int fd; if ((fd = open((TT.fname = "/dev/rtc"), flag)) != -1) return fd; - else if ((fd = open_wall_clock_rtc(flag)) != -1) return fd; - else TT.fname = "/dev/misc/rtc"; + TT.fname = 0; + dirtree_read("/sys/class/rtc", rtc_find); + if (TT.fname) return xopen(TT.fname, flag); + TT.fname = "/dev/misc/rtc"; } + return xopen(TT.fname, flag); } -static time_t get_rtc() +// Get current time in seconds from rtc device. We could get subsecond time by +// waiting for next time change, but haven't implemented that yet. +static time_t get_rtc_seconds() { struct tm time; time_t tm; - char *ptz_old = 0; + char *ptz_old = ptz_old; int fd = rtc_open(O_RDONLY); xioctl(fd, RTC_RD_TIME, &time); close(fd); - if (TT.utc) { - ptz_old = getenv("TZ"); - if (putenv((char*)"TZ=UTC0")) perror_exit("putenv"); - tzset(); - } + + if (TT.utc) ptz_old = xtzset("UTC0"); if ((tm = mktime(&time)) < 0) error_exit("mktime failed"); if (TT.utc) { - if (unsetenv("TZ") < 0) perror_exit("unsetenv"); - if (ptz_old && putenv(ptz_old - 3)) perror_exit("putenv"); - tzset(); + free(xtzset(ptz_old)); + free(ptz_old); } - return tm; -} -static void set_sysclock_from_hwclock() -{ - struct timezone tmzone; - struct timeval tmval; - - tmzone.tz_minuteswest = timezone / 60 - 60 * daylight; - tmzone.tz_dsttime = 0; - tmval.tv_sec = get_rtc(); - tmval.tv_usec = 0; - if (settimeofday(&tmval, &tmzone) < 0) perror_exit("settimeofday"); -} - -static void set_hwclock_from_sysclock() -{ - struct timeval tmval; - struct tm time; - int fd = rtc_open(O_WRONLY); - - if (gettimeofday(&tmval, NULL) < 0) perror_exit("gettimeofday"); - // converting a time value to broken-down UTC time - if (TT.utc && !gmtime_r((time_t*)&tmval.tv_sec, &time)) - error_exit("gmtime_r failed"); - // converting a time value to a broken-down localtime - else if (!(localtime_r((time_t*)&tmval.tv_sec, &time))) - error_exit("localtime_r failed"); - - /* The value of tm_isdst will positive if daylight saving time is in effect, - * zero if it is not and negative if the information is not available. - * */ - time.tm_isdst = 0; - xioctl(fd, RTC_SET_TIME, &time); - close(fd); -} - -static void set_sysclock_timezone() -{ - struct timezone tmzone; - struct timeval tmval; - struct tm *pb; - - if (gettimeofday(&tmval, NULL) < 0) perror_exit("gettimeofday"); - if (!(pb = localtime(&tmval.tv_sec))) error_exit("localtime failed"); - // extern long timezone => defined in header sys/time.h - tmzone.tz_minuteswest = timezone / 60; - if (pb->tm_isdst) tmzone.tz_minuteswest -= 60; - tmzone.tz_dsttime = 0; // daylight saving time is not in effect - if (gettimeofday(&tmval, NULL) < 0) perror_exit("gettimeofday"); - if (!TT.utc) tmval.tv_sec += tmzone.tz_minuteswest * 60; - if (settimeofday(&tmval, &tmzone) < 0) perror_exit("settimeofday"); + return tm; } void hwclock_main() { - // check for UTC - if (!(toys.optflags & FLAG_u)) { + struct timezone tzone; + struct timeval timeval; + + // check for Grenich Mean Time + if (toys.optflags & FLAG_u) TT.utc = 1; + else { FILE *fp = fopen("/etc/adjtime", "r"); if (fp) { - char *line = NULL; - size_t st; - - while (0 < getline(&line, &st, fp)) { - if (!strncmp(line, "UTC", 3)) { - TT.utc = 1; - break; - } + for (;;) { + char *line = 0; + + if (getline(&line, (void *)toybuf, fp) <= 0) break; + TT.utc += !strncmp(line, "UTC", 3); free(line); } fclose(fp); } - } else TT.utc = 1; - - if (toys.optflags & FLAG_w) set_hwclock_from_sysclock(); - else if (toys.optflags & FLAG_s) set_sysclock_from_hwclock(); - else if (toys.optflags & FLAG_t) set_sysclock_timezone(); - else if ((toys.optflags & FLAG_r) || (toys.optflags & FLAG_l) - || !*toys.optargs) - { - time_t tm = get_rtc(); - char *s, *pctm = ctime(&tm); - - // ctime() is defined as equivalent to asctime(localtime(t)), - // which is defined to overflow its buffer rather than return NULL. - // if (!pctm) error_exit("can't happen"); - if ((s = strrchr(pctm, '\n'))) *s = '\0'; + } + + if (toys.optflags & FLAG_w) { + struct tm tm; + int fd = rtc_open(O_WRONLY); + + if (gettimeofday(&timeval, 0)) perror_exit("gettimeofday"); + if ((TT.utc ? gmtime_r : localtime_r)(&timeval.tv_sec, &tm)) + error_exit("timeval"); + + /* The value of tm_isdst will positive if daylight saving time is in effect, + * zero if it is not and negative if the information is not available. + * */ + tm.tm_isdst = 0; + xioctl(fd, RTC_SET_TIME, &time); + close(fd); + } else if (toys.optflags & FLAG_s) { + tzone.tz_minuteswest = timezone / 60 - 60 * daylight; + tzone.tz_dsttime = 0; + timeval.tv_sec = get_rtc_seconds(); + timeval.tv_usec = 0; + if (settimeofday(&timeval, &tzone) < 0) perror_exit("settimeofday"); + } else if (toys.optflags & FLAG_t) { + struct tm *pb; + + if (gettimeofday(&timeval, NULL) < 0) perror_exit("gettimeofday"); + if (!(pb = localtime(&timeval.tv_sec))) error_exit("localtime failed"); + // extern long timezone is defined in header sys/time.h + tzone.tz_minuteswest = timezone / 60; + if (pb->tm_isdst) tzone.tz_minuteswest -= 60; + tzone.tz_dsttime = 0; // daylight saving time is not in effect + if (gettimeofday(&timeval, NULL) < 0) perror_exit("gettimeofday"); + if (!TT.utc) timeval.tv_sec += tzone.tz_minuteswest * 60; + if (settimeofday(&timeval, &tzone) < 0) perror_exit("settimeofday"); + } else { + time_t tm = get_rtc_seconds(); + char *pctm = ctime(&tm), *s = strrchr(pctm, '\n'); + + if (s) *s = '\0'; // TODO: implement this. xprintf("%s 0.000000 seconds\n", pctm); } |