7 |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
8 |
*/ |
*/ |
9 |
|
|
|
|
|
|
#include <sys/ioctl.h> |
|
10 |
#include <sys/utsname.h> |
#include <sys/utsname.h> |
11 |
#include <getopt.h> |
#include "libbb.h" |
12 |
#include "busybox.h" |
#include "rtc_.h" |
|
|
|
|
/* Copied from linux/rtc.h to eliminate the kernel dependency */ |
|
|
struct linux_rtc_time { |
|
|
int tm_sec; |
|
|
int tm_min; |
|
|
int tm_hour; |
|
|
int tm_mday; |
|
|
int tm_mon; |
|
|
int tm_year; |
|
|
int tm_wday; |
|
|
int tm_yday; |
|
|
int tm_isdst; |
|
|
}; |
|
|
|
|
|
#define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */ |
|
|
#define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */ |
|
13 |
|
|
14 |
#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS |
#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS |
15 |
# ifndef _GNU_SOURCE |
# ifndef _GNU_SOURCE |
17 |
# endif |
# endif |
18 |
#endif |
#endif |
19 |
|
|
20 |
static int xopen_rtc(int flags) |
static const char *rtcname; |
|
{ |
|
|
int rtc; |
|
|
rtc = open("/dev/rtc", flags); |
|
|
if (rtc < 0) { |
|
|
rtc = open("/dev/misc/rtc", flags); |
|
|
if (rtc < 0) |
|
|
bb_perror_msg_and_die("cannot access RTC"); |
|
|
} |
|
|
return rtc; |
|
|
} |
|
21 |
|
|
22 |
static time_t read_rtc(int utc) |
static time_t read_rtc(int utc) |
23 |
{ |
{ |
24 |
struct tm tm; |
time_t ret; |
25 |
char *oldtz = 0; |
int fd; |
|
time_t t = 0; |
|
|
int rtc = xopen_rtc(O_RDONLY); |
|
|
|
|
|
memset(&tm, 0, sizeof(struct tm)); |
|
|
if (ioctl(rtc, RTC_RD_TIME, &tm) < 0 ) |
|
|
bb_perror_msg_and_die("cannot read time from RTC"); |
|
|
tm.tm_isdst = -1; /* not known */ |
|
26 |
|
|
27 |
close(rtc); |
fd = rtc_xopen(&rtcname, O_RDONLY); |
28 |
|
ret = rtc_read_time(fd, utc); |
29 |
|
close(fd); |
30 |
|
|
31 |
if (utc) { |
return ret; |
|
oldtz = getenv("TZ"); |
|
|
setenv("TZ", "UTC 0", 1); |
|
|
tzset(); |
|
|
} |
|
|
|
|
|
t = mktime(&tm); |
|
|
|
|
|
if (utc) { |
|
|
if (oldtz) |
|
|
setenv("TZ", oldtz, 1); |
|
|
else |
|
|
unsetenv("TZ"); |
|
|
tzset(); |
|
|
} |
|
|
return t; |
|
32 |
} |
} |
33 |
|
|
34 |
static void write_rtc(time_t t, int utc) |
static void write_rtc(time_t t, int utc) |
35 |
{ |
{ |
36 |
struct tm tm; |
struct tm tm; |
37 |
int rtc = xopen_rtc(O_WRONLY); |
int rtc = rtc_xopen(&rtcname, O_WRONLY); |
38 |
|
|
39 |
tm = *(utc ? gmtime(&t) : localtime(&t)); |
tm = *(utc ? gmtime(&t) : localtime(&t)); |
40 |
tm.tm_isdst = 0; |
tm.tm_isdst = 0; |
41 |
|
|
42 |
if (ioctl(rtc, RTC_SET_TIME, &tm) < 0) |
xioctl(rtc, RTC_SET_TIME, &tm); |
|
bb_perror_msg_and_die("cannot set the RTC time"); |
|
43 |
|
|
44 |
close(rtc); |
close(rtc); |
45 |
} |
} |
46 |
|
|
47 |
static int show_clock(int utc) |
static void show_clock(int utc) |
48 |
{ |
{ |
49 |
struct tm *ptm; |
//struct tm *ptm; |
50 |
time_t t; |
time_t t; |
51 |
RESERVE_CONFIG_BUFFER(buffer, 64); |
char *cp; |
52 |
|
|
53 |
t = read_rtc(utc); |
t = read_rtc(utc); |
54 |
ptm = localtime(&t); /* Sets 'tzname[]' */ |
//ptm = localtime(&t); /* Sets 'tzname[]' */ |
55 |
|
|
56 |
safe_strncpy(buffer, ctime(&t), 64); |
cp = ctime(&t); |
57 |
if (buffer[0]) |
if (cp[0]) |
58 |
buffer[strlen(buffer) - 1] = 0; |
cp[strlen(cp) - 1] = '\0'; |
|
|
|
|
//printf("%s %.6f seconds %s\n", buffer, 0.0, utc ? "" : (ptm->tm_isdst ? tzname[1] : tzname[0])); |
|
|
printf( "%s %.6f seconds\n", buffer, 0.0); |
|
|
RELEASE_CONFIG_BUFFER(buffer); |
|
59 |
|
|
60 |
return 0; |
//printf("%s %.6f seconds %s\n", cp, 0.0, utc ? "" : (ptm->tm_isdst ? tzname[1] : tzname[0])); |
61 |
|
printf("%s 0.000000 seconds\n", cp); |
62 |
} |
} |
63 |
|
|
64 |
static int to_sys_clock(int utc) |
static void to_sys_clock(int utc) |
65 |
{ |
{ |
66 |
struct timeval tv = { 0, 0 }; |
struct timeval tv; |
67 |
const struct timezone tz = { timezone/60 - 60*daylight, 0 }; |
const struct timezone tz = { timezone/60 - 60*daylight, 0 }; |
68 |
|
|
69 |
tv.tv_sec = read_rtc(utc); |
tv.tv_sec = read_rtc(utc); |
70 |
|
tv.tv_usec = 0; |
71 |
if (settimeofday(&tv, &tz)) |
if (settimeofday(&tv, &tz)) |
72 |
bb_perror_msg_and_die("settimeofday() failed"); |
bb_perror_msg_and_die("settimeofday() failed"); |
|
|
|
|
return 0; |
|
73 |
} |
} |
74 |
|
|
75 |
static int from_sys_clock(int utc) |
static void from_sys_clock(int utc) |
76 |
{ |
{ |
77 |
struct timeval tv = { 0, 0 }; |
struct timeval tv; |
|
struct timezone tz = { 0, 0 }; |
|
|
|
|
|
if (gettimeofday(&tv, &tz)) |
|
|
bb_perror_msg_and_die("gettimeofday() failed"); |
|
78 |
|
|
79 |
|
gettimeofday(&tv, NULL); |
80 |
|
//if (gettimeofday(&tv, NULL)) |
81 |
|
// bb_perror_msg_and_die("gettimeofday() failed"); |
82 |
write_rtc(tv.tv_sec, utc); |
write_rtc(tv.tv_sec, utc); |
|
return 0; |
|
|
} |
|
|
|
|
|
#ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS |
|
|
# define ADJTIME_PATH "/var/lib/hwclock/adjtime" |
|
|
#else |
|
|
# define ADJTIME_PATH "/etc/adjtime" |
|
|
#endif |
|
|
static int check_utc(void) |
|
|
{ |
|
|
int utc = 0; |
|
|
FILE *f = fopen(ADJTIME_PATH, "r"); |
|
|
|
|
|
if (f) { |
|
|
RESERVE_CONFIG_BUFFER(buffer, 128); |
|
|
|
|
|
while (fgets(buffer, sizeof(buffer), f)) { |
|
|
int len = strlen(buffer); |
|
|
|
|
|
while (len && isspace(buffer[len - 1])) |
|
|
len--; |
|
|
|
|
|
buffer[len] = 0; |
|
|
|
|
|
if (strncmp(buffer, "UTC", 3) == 0 ) { |
|
|
utc = 1; |
|
|
break; |
|
|
} |
|
|
} |
|
|
fclose(f); |
|
|
RELEASE_CONFIG_BUFFER(buffer); |
|
|
} |
|
|
return utc; |
|
83 |
} |
} |
84 |
|
|
85 |
#define HWCLOCK_OPT_LOCALTIME 0x01 |
#define HWCLOCK_OPT_LOCALTIME 0x01 |
87 |
#define HWCLOCK_OPT_SHOW 0x04 |
#define HWCLOCK_OPT_SHOW 0x04 |
88 |
#define HWCLOCK_OPT_HCTOSYS 0x08 |
#define HWCLOCK_OPT_HCTOSYS 0x08 |
89 |
#define HWCLOCK_OPT_SYSTOHC 0x10 |
#define HWCLOCK_OPT_SYSTOHC 0x10 |
90 |
|
#define HWCLOCK_OPT_RTCFILE 0x20 |
91 |
|
|
92 |
int hwclock_main(int argc, char **argv ) |
int hwclock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
93 |
|
int hwclock_main(int argc UNUSED_PARAM, char **argv) |
94 |
{ |
{ |
95 |
unsigned opt; |
unsigned opt; |
96 |
int utc; |
int utc; |
97 |
|
|
98 |
#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS |
#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS |
99 |
static const struct option hwclock_long_options[] = { |
static const char hwclock_longopts[] ALIGN1 = |
100 |
{ "localtime", 0, 0, 'l' }, |
"localtime\0" No_argument "l" |
101 |
{ "utc", 0, 0, 'u' }, |
"utc\0" No_argument "u" |
102 |
{ "show", 0, 0, 'r' }, |
"show\0" No_argument "r" |
103 |
{ "hctosys", 0, 0, 's' }, |
"hctosys\0" No_argument "s" |
104 |
{ "systohc", 0, 0, 'w' }, |
"systohc\0" No_argument "w" |
105 |
{ 0, 0, 0, 0 } |
"file\0" Required_argument "f" |
106 |
}; |
; |
107 |
applet_long_options = hwclock_long_options; |
applet_long_options = hwclock_longopts; |
108 |
#endif |
#endif |
109 |
opt_complementary = "?:r--ws:w--rs:s--wr:l--u:u--l"; |
opt_complementary = "r--ws:w--rs:s--wr:l--u:u--l"; |
110 |
opt = getopt32(argc, argv, "lursw"); |
opt = getopt32(argv, "lurswf:", &rtcname); |
111 |
|
|
112 |
/* If -u or -l wasn't given check if we are using utc */ |
/* If -u or -l wasn't given check if we are using utc */ |
113 |
if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) |
if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) |
114 |
utc = opt & HWCLOCK_OPT_UTC; |
utc = (opt & HWCLOCK_OPT_UTC); |
115 |
else |
else |
116 |
utc = check_utc(); |
utc = rtc_adjtime_is_utc(); |
117 |
|
|
118 |
if (opt & HWCLOCK_OPT_HCTOSYS) { |
if (opt & HWCLOCK_OPT_HCTOSYS) |
119 |
return to_sys_clock(utc); |
to_sys_clock(utc); |
120 |
} |
else if (opt & HWCLOCK_OPT_SYSTOHC) |
121 |
else if (opt & HWCLOCK_OPT_SYSTOHC) { |
from_sys_clock(utc); |
122 |
return from_sys_clock(utc); |
else |
|
} else { |
|
123 |
/* default HWCLOCK_OPT_SHOW */ |
/* default HWCLOCK_OPT_SHOW */ |
124 |
return show_clock(utc); |
show_clock(utc); |
125 |
} |
|
126 |
|
return 0; |
127 |
} |
} |