diff options
Diffstat (limited to 'toys/other')
-rw-r--r-- | toys/other/hwclock.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/toys/other/hwclock.c b/toys/other/hwclock.c new file mode 100644 index 00000000..75e06416 --- /dev/null +++ b/toys/other/hwclock.c @@ -0,0 +1,136 @@ +/* hwclock.c - get and set the hwclock + * + * Copyright 2014 Bilal Qureshi <bilal.jmi@gmail.com> + * + * 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)s(hctosys)r(show)w(systohc)[-ul][!rtsw]", TOYFLAG_USR|TOYFLAG_BIN)) + +config HWCLOCK + bool "hwclock" + default y + help + usage: hwclock [-rswtluf] + + -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) + -t Set the system time based on the current timezone (--systz) + -u Hardware clock uses UTC (--utc) + -w Set hardware clock from system time (--systohc) +*/ + +#define FOR_hwclock +#include "toys.h" +#include <linux/rtc.h> + +GLOBALS( + char *fname; + + int utc; +) + +static int rtc_find(struct dirtree* node) +{ + FILE *fp; + + if (!node->parent) return DIRTREE_RECURSE; + + snprintf(toybuf, sizeof(toybuf), "/sys/class/rtc/%s/hctosys", node->name); + fp = fopen(toybuf, "r"); + if (fp) { + 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; +} + +void hwclock_main() +{ + struct timezone tzone; + struct timeval timeval; + struct tm tm; + time_t time; + int fd = -1; + + // check for Grenich Mean Time + if (toys.optflags & FLAG_u) TT.utc = 1; + else { + FILE *fp; + char *s = 0; + + for (fp = fopen("/etc/adjtime", "r"); + fp && getline(&s, (void *)toybuf, fp)>0; + free(s), s = 0) TT.utc += !strncmp(s, "UTC", 3); + if (fp) fclose(fp); + } + + if (!(toys.optflags&FLAG_t)) { + int w = toys.optflags & FLAG_w, flag = O_WRONLY*w; + + // Open /dev/rtc (if your system has no /dev/rtc symlink, search for it). + if (!TT.fname && (fd = open("/dev/rtc", flag)) == -1) { + dirtree_read("/sys/class/rtc", rtc_find); + if (!TT.fname) TT.fname = "/dev/misc/rtc"; + } + if (fd == -1) fd = xopen(TT.fname, flag); + + // Get current time in seconds from rtc device. todo: get subsecond time + if (!w) { + char *s = s; + + xioctl(fd, RTC_RD_TIME, &tm); + if (TT.utc) s = xtzset("UTC0"); + if ((time = mktime(&tm)) < 0) error_exit("mktime failed"); + if (TT.utc) { + free(xtzset(s)); + free(s); + } + } + } + + if (toys.optflags & (FLAG_w|FLAG_t)) { + if (gettimeofday(&timeval, 0)) perror_exit("gettimeofday failed"); + if (!(TT.utc ? gmtime_r : localtime_r)(&timeval.tv_sec, &tm)) + error_exit(TT.utc ? "gmtime_r failed" : "localtime_r failed"); + } + + if (toys.optflags & FLAG_w) { + /* 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. + * todo: so why isn't this negative...? */ + tm.tm_isdst = 0; + xioctl(fd, RTC_SET_TIME, &tm); + } else if (toys.optflags & FLAG_s) { + tzone.tz_minuteswest = timezone / 60 - 60 * daylight; + timeval.tv_sec = time; + timeval.tv_usec = 0; // todo: fixit + } else if (toys.optflags & FLAG_t) { + // Adjust seconds for timezone and daylight saving time + // extern long timezone is defined in header sys/time.h + tzone.tz_minuteswest = timezone / 60; + if (tm.tm_isdst) tzone.tz_minuteswest -= 60; + if (!TT.utc) timeval.tv_sec += tzone.tz_minuteswest * 60; + } else { + char *c = ctime(&time), *s = strrchr(c, '\n'); + + if (s) *s = '\0'; + // TODO: implement this. + xprintf("%s 0.000000 seconds\n", c); + } + if (toys.optflags & (FLAG_t|FLAG_s)) { + tzone.tz_dsttime = 0; + if (settimeofday(&timeval, &tzone)) perror_exit("settimeofday failed"); + } + + if (fd != -1) close(fd); +} |