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