blob: 488149df2cd1ea70abaa758410a2e2390c3bf88c
1 | /* |
2 | * Common RTC functions |
3 | * |
4 | * Licensed under GPLv2, see file LICENSE in this source tree. |
5 | */ |
6 | |
7 | #include "libbb.h" |
8 | #include "rtc_.h" |
9 | |
10 | #if ENABLE_FEATURE_HWCLOCK_ADJTIME_FHS |
11 | # define ADJTIME_PATH "/var/lib/hwclock/adjtime" |
12 | #else |
13 | # define ADJTIME_PATH "/etc/adjtime" |
14 | #endif |
15 | |
16 | int FAST_FUNC rtc_adjtime_is_utc(void) |
17 | { |
18 | int utc = 0; |
19 | FILE *f = fopen_for_read(ADJTIME_PATH); |
20 | |
21 | if (f) { |
22 | char buffer[128]; |
23 | |
24 | while (fgets(buffer, sizeof(buffer), f)) { |
25 | if (is_prefixed_with(buffer, "UTC")) { |
26 | utc = 1; |
27 | break; |
28 | } |
29 | } |
30 | fclose(f); |
31 | } |
32 | |
33 | return utc; |
34 | } |
35 | |
36 | /* rtc opens are exclusive. |
37 | * Try to run two "hwclock -w" at the same time to see it. |
38 | * Users wouldn't expect that to fail merely because /dev/rtc |
39 | * was momentarily busy, let's try a bit harder on errno == EBUSY. |
40 | */ |
41 | static int open_loop_on_busy(const char *name, int flags) |
42 | { |
43 | int rtc; |
44 | /* |
45 | * Tested with two parallel "hwclock -w" loops. |
46 | * With try = 10, no failures with 2x1000000 loop iterations. |
47 | */ |
48 | int try = 1000 / 20; |
49 | again: |
50 | errno = 0; |
51 | rtc = open(name, flags); |
52 | if (errno == EBUSY) { |
53 | usleep(20 * 1000); |
54 | if (--try != 0) |
55 | goto again; |
56 | /* EBUSY. Last try, exit on error instead of returning -1 */ |
57 | return xopen(name, flags); |
58 | } |
59 | return rtc; |
60 | } |
61 | |
62 | /* Never fails */ |
63 | int FAST_FUNC rtc_xopen(const char **default_rtc, int flags) |
64 | { |
65 | int rtc; |
66 | const char *name = |
67 | "/dev/rtc""\0" |
68 | "/dev/rtc0""\0" |
69 | "/dev/misc/rtc""\0"; |
70 | |
71 | if (!*default_rtc) |
72 | goto try_name; |
73 | name = ""; /*else: we have rtc name, don't try other names */ |
74 | |
75 | for (;;) { |
76 | rtc = open_loop_on_busy(*default_rtc, flags); |
77 | if (rtc >= 0) |
78 | return rtc; |
79 | if (!name[0]) |
80 | return xopen(*default_rtc, flags); |
81 | try_name: |
82 | *default_rtc = name; |
83 | name += strlen(name) + 1; |
84 | } |
85 | } |
86 | |
87 | void FAST_FUNC rtc_read_tm(struct tm *ptm, int fd) |
88 | { |
89 | memset(ptm, 0, sizeof(*ptm)); |
90 | xioctl(fd, RTC_RD_TIME, ptm); |
91 | ptm->tm_isdst = -1; /* "not known" */ |
92 | } |
93 | |
94 | time_t FAST_FUNC rtc_tm2time(struct tm *ptm, int utc) |
95 | { |
96 | char *oldtz = NULL; |
97 | time_t t; |
98 | |
99 | if (utc) { |
100 | oldtz = getenv("TZ"); |
101 | putenv((char*)"TZ=UTC0"); |
102 | tzset(); |
103 | } |
104 | |
105 | t = mktime(ptm); |
106 | |
107 | if (utc) { |
108 | unsetenv("TZ"); |
109 | if (oldtz) |
110 | putenv(oldtz - 3); |
111 | tzset(); |
112 | } |
113 | |
114 | return t; |
115 | } |
116 |