1/* hwclock.c - get and set the hwclock 2 * 3 * Copyright 2014 Bilal Qureshi <bilal.jmi@gmail.com> 4 * 5 * No standard, but see Documentation/rtc.txt in the linux kernel source.. 6 * 7USE_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)) 8 9config HWCLOCK 10 bool "hwclock" 11 default n 12 help 13 usage: hwclock [-rswtluf] 14 15 -f FILE Use specified device file instead of /dev/rtc (--rtc) 16 -l Hardware clock uses localtime (--localtime) 17 -r Show hardware clock time (--show) 18 -s Set system time from hardware clock (--hctosys) 19 -t Set the system time based on the current timezone (--systz) 20 -u Hardware clock uses UTC (--utc) 21 -w Set hardware clock from system time (--systohc) 22*/ 23 24#define FOR_hwclock 25#include "toys.h" 26#include <linux/rtc.h> 27 28GLOBALS( 29 char *fname; 30 31 int utc; 32) 33 34static int rtc_find(struct dirtree* node) 35{ 36 FILE *fp; 37 38 if (!node->parent) return DIRTREE_RECURSE; 39 40 snprintf(toybuf, sizeof(toybuf), "/sys/class/rtc/%s/hctosys", node->name); 41 fp = fopen(toybuf, "r"); 42 if (fp) { 43 int hctosys = 0, items = fscanf(fp, "%d", &hctosys); 44 45 fclose(fp); 46 if (items == 1 && hctosys == 1) { 47 snprintf(toybuf, sizeof(toybuf), "/dev/%s", node->name); 48 TT.fname = toybuf; 49 50 return DIRTREE_ABORT; 51 } 52 } 53 54 return 0; 55} 56 57void hwclock_main() 58{ 59 struct timezone tzone; 60 struct timeval timeval; 61 struct tm tm; 62 time_t time; 63 int fd = -1; 64 65 // check for Grenich Mean Time 66 if (toys.optflags & FLAG_u) TT.utc = 1; 67 else { 68 FILE *fp; 69 char *s = 0; 70 71 for (fp = fopen("/etc/adjtime", "r"); 72 fp && getline(&s, (void *)toybuf, fp)>0; 73 free(s), s = 0) TT.utc += !strncmp(s, "UTC", 3); 74 if (fp) fclose(fp); 75 } 76 77 if (!(toys.optflags&FLAG_t)) { 78 int w = toys.optflags & FLAG_w, flag = O_WRONLY*w; 79 80 // Open /dev/rtc (if your system has no /dev/rtc symlink, search for it). 81 if (!TT.fname && (fd = open("/dev/rtc", flag)) == -1) { 82 dirtree_read("/sys/class/rtc", rtc_find); 83 if (!TT.fname) TT.fname = "/dev/misc/rtc"; 84 } 85 if (fd == -1) fd = xopen(TT.fname, flag); 86 87 // Get current time in seconds from rtc device. todo: get subsecond time 88 if (!w) { 89 char *s = s; 90 91 xioctl(fd, RTC_RD_TIME, &tm); 92 if (TT.utc) s = xtzset("UTC0"); 93 if ((time = mktime(&tm)) < 0) goto bad; 94 if (TT.utc) { 95 free(xtzset(s)); 96 free(s); 97 } 98 } 99 } 100 101 if (toys.optflags & (FLAG_w|FLAG_t)) 102 if (gettimeofday(&timeval, 0) 103 || (TT.utc ? gmtime_r : localtime_r)(&timeval.tv_sec, &tm)) goto bad; 104 105 if (toys.optflags & FLAG_w) { 106 /* The value of tm_isdst will positive if daylight saving time is in effect, 107 * zero if it is not and negative if the information is not available. 108 * todo: so why isn't this negative...? */ 109 tm.tm_isdst = 0; 110 xioctl(fd, RTC_SET_TIME, &time); 111 } else if (toys.optflags & FLAG_s) { 112 tzone.tz_minuteswest = timezone / 60 - 60 * daylight; 113 timeval.tv_sec = time; 114 timeval.tv_usec = 0; // todo: fixit 115 } else if (toys.optflags & FLAG_t) { 116 // Adjust seconds for timezone and daylight saving time 117 // extern long timezone is defined in header sys/time.h 118 tzone.tz_minuteswest = timezone / 60; 119 if (tm.tm_isdst) tzone.tz_minuteswest -= 60; 120 if (!TT.utc) timeval.tv_sec += tzone.tz_minuteswest * 60; 121 } else { 122 char *c = ctime(&time), *s = strrchr(c, '\n'); 123 124 if (s) *s = '\0'; 125 // TODO: implement this. 126 xprintf("%s 0.000000 seconds\n", c); 127 } 128 if (toys.optflags & (FLAG_t|FLAG_s)) { 129 tzone.tz_dsttime = 0; 130 if (settimeofday(&timeval, &tzone)) goto bad; 131 } 132 133 if (fd != -1) close(fd); 134 135 return; 136bad: 137 perror_exit("failed"); 138} 139