• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *
7  * TODO: get/set subsecond time
8 USE_HWCLOCK(NEWTOY(hwclock, ">0(fast)f(rtc):u(utc)l(localtime)t(systz)s(hctosys)r(show)w(systohc)[-ul][!rtsw]", TOYFLAG_SBIN))
9 
10 config HWCLOCK
11   bool "hwclock"
12   default y
13   help
14     usage: hwclock [-rswtlu] [-f FILE]
15 
16     Get/set the hardware clock. Default is hwclock -ruf /dev/rtc0
17 
18     -f	Use specified device FILE instead of /dev/rtc0 (--rtc)
19     -l	Hardware clock uses localtime (--localtime)
20     -r	Show hardware clock time (--show)
21     -s	Set system time from hardware clock (--hctosys)
22     -t	Inform kernel of non-UTC clock's timezone so it returns UTC (--systz)
23     -u	Hardware clock uses UTC (--utc)
24     -w	Set hardware clock from system time (--systohc)
25 */
26 
27 
28 // Bug workaround for musl commit 5a105f19b5aa which removed a symbol the
29 // kernel headers have. (Can't copy it here, varies wildly by architecture.)
30 #if __has_include(<asm/unistd.h>)
31 #include <asm/unistd.h>
32 #endif
33 
34 #define FOR_hwclock
35 #include "toys.h"
36 #include <linux/rtc.h>
37 
GLOBALS(char * f;)38 GLOBALS(
39   char *f;
40 )
41 
42 // Bug workaround for musl commit 2c2c3605d3b3 which rewrote the syscall
43 // wrapper to not use the syscall, which is the only way to set kernel's sys_tz
44 #define settimeofday(x, tz) syscall(__NR_settimeofday, (void *)0, (void *)tz)
45 
46 void hwclock_main()
47 {
48   struct timezone tz = {0};
49   struct timespec ts = {0};
50   struct tm tm;
51   int fd = -1;
52 
53   // -t without -u implies -l
54   if (FLAG(t)&&!FLAG(u)) toys.optflags |= FLAG_l;
55   if (FLAG(l)) {
56     // sets globals timezone and daylight from sys/time.h
57     // Handle dst adjustment ourselves. (Rebooting during dst transition is
58     // just conceptually unpleasant, linux uses UTC for a reason.)
59     tzset();
60     tz.tz_minuteswest = timezone/60 - 60*daylight;
61   }
62 
63   if (!FLAG(t)) {
64     fd = xopen(TT.f ? : "/dev/rtc0", O_WRONLY*FLAG(w));
65 
66     // Get current time in seconds from rtc device.
67     if (!FLAG(w)) {
68       xioctl(fd, RTC_RD_TIME, &tm);
69       ts.tv_sec = xmktime(&tm, !FLAG(l));
70     }
71   }
72 
73   if (FLAG(w) || FLAG(t)) {
74     if (FLAG(w)) {
75       if (clock_gettime(CLOCK_REALTIME, &ts)) perror_exit("clock_gettime");
76       if (!(FLAG(l) ? localtime_r : gmtime_r)(&ts.tv_sec, &tm))
77         error_exit("%s failed", FLAG(l) ? "localtime_r" : "gmtime_r");
78       xioctl(fd, RTC_SET_TIME, &tm);
79     }
80     if (settimeofday(0, &tz)) perror_exit("settimeofday");
81   } else if (FLAG(s)) {
82     if (clock_settime(CLOCK_REALTIME, &ts)) perror_exit("clock_settime");
83   } else {
84     strftime(toybuf, sizeof(toybuf), "%F %T%z", &tm);
85     xputs(toybuf);
86   }
87 
88   if (CFG_TOYBOX_FREE) xclose(fd);
89 }
90