Home | History | Annotate | Download | only in other
      1 /* hwclock.c - get and set the hwclock
      2  *
      3  * Copyright 2014 Bilal Qureshi <bilal.jmi (at) gmail.com>
      4  *
      5  * No standard, but see Documentation/rtc.txt in the linux kernel source..
      6  *
      7 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))
      8 
      9 config HWCLOCK
     10   bool "hwclock"
     11   default y
     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 
     28 GLOBALS(
     29   char *fname;
     30 
     31   int utc;
     32 )
     33 
     34 static int rtc_find(struct dirtree* node)
     35 {
     36   FILE *fp;
     37 
     38   if (!node->parent) return DIRTREE_RECURSE;
     39 
     40   sprintf(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       sprintf(toybuf, "/dev/%s", node->name);
     48       TT.fname = toybuf;
     49 
     50       return DIRTREE_ABORT;
     51     }
     52   }
     53 
     54   return 0;
     55 }
     56 
     57 void 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) error_exit("mktime failed");
     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)) perror_exit("gettimeofday failed");
    103     if (!(TT.utc ? gmtime_r : localtime_r)(&timeval.tv_sec, &tm))
    104       error_exit(TT.utc ? "gmtime_r failed" : "localtime_r failed");
    105   }
    106 
    107   if (toys.optflags & FLAG_w) {
    108     /* The value of tm_isdst is positive if daylight saving time is in effect,
    109      * zero if it is not and negative if the information is not available.
    110      * todo: so why isn't this negative...? */
    111     tm.tm_isdst = 0;
    112     xioctl(fd, RTC_SET_TIME, &tm);
    113   } else if (toys.optflags & FLAG_s) {
    114     tzone.tz_minuteswest = timezone / 60 - 60 * daylight;
    115     timeval.tv_sec = time;
    116     timeval.tv_usec = 0; // todo: fixit
    117   } else if (toys.optflags & FLAG_t) {
    118     // Adjust seconds for timezone and daylight saving time
    119     // extern long timezone is defined in header sys/time.h
    120     tzone.tz_minuteswest = timezone / 60;
    121     if (tm.tm_isdst) tzone.tz_minuteswest -= 60;
    122     if (!TT.utc) timeval.tv_sec += tzone.tz_minuteswest * 60;
    123   } else {
    124     char *c = ctime(&time), *s = strrchr(c, '\n');
    125 
    126     if (s) *s = '\0';
    127     // TODO: implement this.
    128     xprintf("%s  0.000000 seconds\n", c);
    129   }
    130   if (toys.optflags & (FLAG_t|FLAG_s)) {
    131     tzone.tz_dsttime = 0;
    132     if (settimeofday(&timeval, &tzone)) perror_exit("settimeofday failed");
    133   }
    134 
    135   if (fd != -1) close(fd);
    136 }
    137