/* hwclock.c - get and set the hwclock * * Copyright 2014 Bilal Qureshi * * No Standard. * USE_HWCLOCK(NEWTOY(hwclock, "f(rtc):u(utc)l(localtime)t(systz)w(systohc)s(hctosys)r(show)[!ul][!rs][!rw][!rt][!sw][!st][!wt]", TOYFLAG_USR|TOYFLAG_BIN)) config HWCLOCK bool "hwclock" default n help usage: hwclock [-r|--show] [-s|--hctosys] [-w|--systohc] [-t|--systz] [-l|--localtime] [-u|--utc] [-f|--rtc FILE] -f FILE Use specified device file (e.g. /dev/rtc2) instead of default -l Assume hardware clock is kept in localtime -r Show hardware clock time -s Set system time from hardware clock -t Set the system time based on the current timezone -u Assume hardware clock is kept in UTC -w Set hardware clock from system time */ #define FOR_hwclock #include "toys.h" #include GLOBALS( char *fname; int utc; ) static int rtc_open(char **dev_rtc, int flag) { if (!*dev_rtc) { int fd; if ((fd = open((*dev_rtc = "/dev/rtc"), flag)) != -1) return fd; else if ((fd = open((*dev_rtc = "/dev/rtc0"), flag)) != -1) return fd; else *dev_rtc = "/dev/misc/rtc"; } return xopen(*dev_rtc, flag); } static time_t get_rtc() { struct tm time; time_t tm; char *ptz_old = NULL; int fd = rtc_open(&TT.fname, O_RDONLY); memset(&time, 0, sizeof(time)); 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 ((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(); } 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(&TT.fname, 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"); } static void rtc_adjtime() { char *line = NULL; int fd = open("/etc/adjtime", O_RDONLY); if (fd != -1) { for (; (line = get_line(fd)); free(line)) { if (!strncmp(line, "UTC", 3)) { TT.utc = 1; break; } } close(fd); } } static void display_hwclock() { time_t tm = get_rtc(); char *s, *pctm = ctime(&tm); if (pctm) { if ((s = strrchr(pctm, '\n'))) *s = '\0'; xprintf("%s 0.000000 seconds\n", pctm); } else error_exit("failed to convert a time value to a date & time string"); } void hwclock_main() { (!(toys.optflags & FLAG_u)) ? rtc_adjtime() : (TT.utc = 1); // check for UTC 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) display_hwclock(); else { toys.exithelp++; error_exit("invalid option '%s'", *toys.optargs); } }