diff options
author | Mike Frysinger <vapier@gentoo.org> | 2008-02-15 02:27:19 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2008-02-15 02:27:19 +0000 |
commit | 6b160e490d4d77596c1603d34d0a1ca0579a82da (patch) | |
tree | 653ab55714a0373751b619144e56b5ea163938f2 | |
parent | be7d2a8ded621a6d62f8caa76f7c51185b166ab1 (diff) | |
download | busybox-w32-6b160e490d4d77596c1603d34d0a1ca0579a82da.tar.gz busybox-w32-6b160e490d4d77596c1603d34d0a1ca0579a82da.tar.bz2 busybox-w32-6b160e490d4d77596c1603d34d0a1ca0579a82da.zip |
split some rtc funcs out of hwclock and into an rtc header/lib so that the new rtcwake applet as well as hwclock can utilize the same code
-rw-r--r-- | include/applets.h | 1 | ||||
-rw-r--r-- | include/rtc_.h | 73 | ||||
-rw-r--r-- | include/usage.h | 23 | ||||
-rw-r--r-- | libbb/Kbuild | 2 | ||||
-rw-r--r-- | libbb/rtc.c | 86 | ||||
-rw-r--r-- | util-linux/Config.in | 6 | ||||
-rw-r--r-- | util-linux/Kbuild | 1 | ||||
-rw-r--r-- | util-linux/hwclock.c | 115 | ||||
-rw-r--r-- | util-linux/rtcwake.c | 209 |
9 files changed, 417 insertions, 99 deletions
diff --git a/include/applets.h b/include/applets.h index b14c20528..90b6e703d 100644 --- a/include/applets.h +++ b/include/applets.h | |||
@@ -293,6 +293,7 @@ USE_RMMOD(APPLET(rmmod, _BB_DIR_SBIN, _BB_SUID_NEVER)) | |||
293 | USE_ROUTE(APPLET(route, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 293 | USE_ROUTE(APPLET(route, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
294 | USE_RPM(APPLET(rpm, _BB_DIR_BIN, _BB_SUID_NEVER)) | 294 | USE_RPM(APPLET(rpm, _BB_DIR_BIN, _BB_SUID_NEVER)) |
295 | USE_RPM2CPIO(APPLET(rpm2cpio, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | 295 | USE_RPM2CPIO(APPLET(rpm2cpio, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) |
296 | USE_RTCWAKE(APPLET(rtcwake, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | ||
296 | USE_RUN_PARTS(APPLET_ODDNAME(run-parts, run_parts, _BB_DIR_BIN, _BB_SUID_NEVER, run_parts)) | 297 | USE_RUN_PARTS(APPLET_ODDNAME(run-parts, run_parts, _BB_DIR_BIN, _BB_SUID_NEVER, run_parts)) |
297 | USE_RUNCON(APPLET(runcon, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | 298 | USE_RUNCON(APPLET(runcon, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) |
298 | USE_RUNLEVEL(APPLET(runlevel, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 299 | USE_RUNLEVEL(APPLET(runlevel, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
diff --git a/include/rtc_.h b/include/rtc_.h new file mode 100644 index 000000000..50b60dd29 --- /dev/null +++ b/include/rtc_.h | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * Common defines/structures/etc... for applets that need to work with the RTC. | ||
3 | * | ||
4 | * Licensed under the GPL-2 or later. | ||
5 | */ | ||
6 | |||
7 | #ifndef _BB_RTC_H_ | ||
8 | #define _BB_RTC_H_ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | extern int rtc_adjtime_is_utc(void); | ||
13 | extern int rtc_xopen(const char *default_rtc, int flags); | ||
14 | extern time_t rtc_read_time(int fd, int utc); | ||
15 | |||
16 | |||
17 | |||
18 | /* | ||
19 | * Everything below this point has been copied from linux/rtc.h | ||
20 | * to eliminate the kernel header dependency | ||
21 | */ | ||
22 | |||
23 | struct linux_rtc_time { | ||
24 | int tm_sec; | ||
25 | int tm_min; | ||
26 | int tm_hour; | ||
27 | int tm_mday; | ||
28 | int tm_mon; | ||
29 | int tm_year; | ||
30 | int tm_wday; | ||
31 | int tm_yday; | ||
32 | int tm_isdst; | ||
33 | }; | ||
34 | |||
35 | struct linux_rtc_wkalrm { | ||
36 | unsigned char enabled; /* 0 = alarm disabled, 1 = alarm enabled */ | ||
37 | unsigned char pending; /* 0 = alarm not pending, 1 = alarm pending */ | ||
38 | struct linux_rtc_time time; /* time the alarm is set to */ | ||
39 | }; | ||
40 | |||
41 | /* | ||
42 | * ioctl calls that are permitted to the /dev/rtc interface, if | ||
43 | * any of the RTC drivers are enabled. | ||
44 | */ | ||
45 | |||
46 | #define RTC_AIE_ON _IO('p', 0x01) /* Alarm int. enable on */ | ||
47 | #define RTC_AIE_OFF _IO('p', 0x02) /* ... off */ | ||
48 | #define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */ | ||
49 | #define RTC_UIE_OFF _IO('p', 0x04) /* ... off */ | ||
50 | #define RTC_PIE_ON _IO('p', 0x05) /* Periodic int. enable on */ | ||
51 | #define RTC_PIE_OFF _IO('p', 0x06) /* ... off */ | ||
52 | #define RTC_WIE_ON _IO('p', 0x0f) /* Watchdog int. enable on */ | ||
53 | #define RTC_WIE_OFF _IO('p', 0x10) /* ... off */ | ||
54 | |||
55 | #define RTC_ALM_SET _IOW('p', 0x07, struct linux_rtc_time) /* Set alarm time */ | ||
56 | #define RTC_ALM_READ _IOR('p', 0x08, struct linux_rtc_time) /* Read alarm time */ | ||
57 | #define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */ | ||
58 | #define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */ | ||
59 | #define RTC_IRQP_READ _IOR('p', 0x0b, unsigned long) /* Read IRQ rate */ | ||
60 | #define RTC_IRQP_SET _IOW('p', 0x0c, unsigned long) /* Set IRQ rate */ | ||
61 | #define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */ | ||
62 | #define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */ | ||
63 | |||
64 | #define RTC_WKALM_SET _IOW('p', 0x0f, struct linux_rtc_wkalrm)/* Set wakeup alarm*/ | ||
65 | #define RTC_WKALM_RD _IOR('p', 0x10, struct linux_rtc_wkalrm)/* Get wakeup alarm*/ | ||
66 | |||
67 | /* interrupt flags */ | ||
68 | #define RTC_IRQF 0x80 /* any of the following is active */ | ||
69 | #define RTC_PF 0x40 | ||
70 | #define RTC_AF 0x20 | ||
71 | #define RTC_UF 0x10 | ||
72 | |||
73 | #endif | ||
diff --git a/include/usage.h b/include/usage.h index 51a92126c..71e1d125f 100644 --- a/include/usage.h +++ b/include/usage.h | |||
@@ -3070,6 +3070,29 @@ USE_FEATURE_BRCTL_FANCY("\n" \ | |||
3070 | #define rpm2cpio_full_usage \ | 3070 | #define rpm2cpio_full_usage \ |
3071 | "Output a cpio archive of the rpm file" | 3071 | "Output a cpio archive of the rpm file" |
3072 | 3072 | ||
3073 | #define rtcwake_trivial_usage \ | ||
3074 | "[-a | -l | -u] [-d DEV] [-m MODE] [-s SECS | -t TIME]" | ||
3075 | #define rtcwake_full_usage \ | ||
3076 | "enter a system sleep state until specified wakeup time\n\n" \ | ||
3077 | USE_GETOPT_LONG( \ | ||
3078 | " -a,--auto Read clock mode from adjtime\n" \ | ||
3079 | " -l,--local Clock is set to local time\n" \ | ||
3080 | " -u,--utc Clock is set to UTC time\n" \ | ||
3081 | " -d,--device=DEV Specify the RTC device\n" \ | ||
3082 | " -m,--mode=MODE Set the sleep state (default: standby)\n" \ | ||
3083 | " -s,--seconds=SEC Set the timeout in SEC seconds from now\n" \ | ||
3084 | " -t,--time=TIME Set the timeout to TIME seconds from epoch" \ | ||
3085 | ) \ | ||
3086 | SKIP_GETOPT_LONG( \ | ||
3087 | " -a Read clock mode from adjtime\n" \ | ||
3088 | " -l Clock is set to local time\n" \ | ||
3089 | " -u Clock is set to UTC time\n" \ | ||
3090 | " -d DEV Specify the RTC device\n" \ | ||
3091 | " -m MODE Set the sleep state (default: standby)\n" \ | ||
3092 | " -s SEC Set the timeout in SEC seconds from now\n" \ | ||
3093 | " -t TIME Set the timeout to TIME seconds from epoch" \ | ||
3094 | ) | ||
3095 | |||
3073 | #define runcon_trivial_usage \ | 3096 | #define runcon_trivial_usage \ |
3074 | "[-c] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] COMMAND [args]\n" \ | 3097 | "[-c] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] COMMAND [args]\n" \ |
3075 | " runcon CONTEXT COMMAND [args]" | 3098 | " runcon CONTEXT COMMAND [args]" |
diff --git a/libbb/Kbuild b/libbb/Kbuild index c4aac95bb..2fb1b2420 100644 --- a/libbb/Kbuild +++ b/libbb/Kbuild | |||
@@ -115,6 +115,8 @@ lib-$(CONFIG_LOGIN) += correct_password.o | |||
115 | lib-$(CONFIG_DF) += find_mount_point.o | 115 | lib-$(CONFIG_DF) += find_mount_point.o |
116 | lib-$(CONFIG_MKFS_MINIX) += find_mount_point.o | 116 | lib-$(CONFIG_MKFS_MINIX) += find_mount_point.o |
117 | lib-$(CONFIG_SELINUX) += selinux_common.o | 117 | lib-$(CONFIG_SELINUX) += selinux_common.o |
118 | lib-$(CONFIG_HWCLOCK) += rtc.o | ||
119 | lib-$(CONFIG_RTCWAKE) += rtc.o | ||
118 | 120 | ||
119 | # We shouldn't build xregcomp.c if we don't need it - this ensures we don't | 121 | # We shouldn't build xregcomp.c if we don't need it - this ensures we don't |
120 | # require regex.h to be in the include dir even if we don't need it thereby | 122 | # require regex.h to be in the include dir even if we don't need it thereby |
diff --git a/libbb/rtc.c b/libbb/rtc.c new file mode 100644 index 000000000..4cbf32206 --- /dev/null +++ b/libbb/rtc.c | |||
@@ -0,0 +1,86 @@ | |||
1 | /* | ||
2 | * Common RTC functions | ||
3 | */ | ||
4 | |||
5 | #include "libbb.h" | ||
6 | #include "rtc_.h" | ||
7 | |||
8 | #if ENABLE_FEATURE_HWCLOCK_ADJTIME_FHS | ||
9 | # define ADJTIME_PATH "/var/lib/hwclock/adjtime" | ||
10 | #else | ||
11 | # define ADJTIME_PATH "/etc/adjtime" | ||
12 | #endif | ||
13 | |||
14 | int rtc_adjtime_is_utc(void) | ||
15 | { | ||
16 | int utc = 0; | ||
17 | FILE *f = fopen(ADJTIME_PATH, "r"); | ||
18 | |||
19 | if (f) { | ||
20 | RESERVE_CONFIG_BUFFER(buffer, 128); | ||
21 | |||
22 | while (fgets(buffer, sizeof(buffer), f)) { | ||
23 | int len = strlen(buffer); | ||
24 | |||
25 | while (len && isspace(buffer[len - 1])) | ||
26 | len--; | ||
27 | |||
28 | buffer[len] = 0; | ||
29 | |||
30 | if (strncmp(buffer, "UTC", 3) == 0) { | ||
31 | utc = 1; | ||
32 | break; | ||
33 | } | ||
34 | } | ||
35 | fclose(f); | ||
36 | |||
37 | RELEASE_CONFIG_BUFFER(buffer); | ||
38 | } | ||
39 | |||
40 | return utc; | ||
41 | } | ||
42 | |||
43 | int rtc_xopen(const char *default_rtc, int flags) | ||
44 | { | ||
45 | int rtc; | ||
46 | |||
47 | if (!default_rtc) { | ||
48 | rtc = open("/dev/rtc", flags); | ||
49 | if (rtc >= 0) | ||
50 | return rtc; | ||
51 | rtc = open("/dev/rtc0", flags); | ||
52 | if (rtc >= 0) | ||
53 | return rtc; | ||
54 | default_rtc = "/dev/misc/rtc"; | ||
55 | } | ||
56 | |||
57 | return xopen(default_rtc, flags); | ||
58 | } | ||
59 | |||
60 | time_t rtc_read_time(int fd, int utc) | ||
61 | { | ||
62 | struct tm tm; | ||
63 | char *oldtz = 0; | ||
64 | time_t t = 0; | ||
65 | |||
66 | memset(&tm, 0, sizeof(struct tm)); | ||
67 | xioctl(fd, RTC_RD_TIME, &tm); | ||
68 | tm.tm_isdst = -1; /* not known */ | ||
69 | |||
70 | if (utc) { | ||
71 | oldtz = getenv("TZ"); | ||
72 | putenv((char*)"TZ=UTC0"); | ||
73 | tzset(); | ||
74 | } | ||
75 | |||
76 | t = mktime(&tm); | ||
77 | |||
78 | if (utc) { | ||
79 | unsetenv("TZ"); | ||
80 | if (oldtz) | ||
81 | putenv(oldtz - 3); | ||
82 | tzset(); | ||
83 | } | ||
84 | |||
85 | return t; | ||
86 | } | ||
diff --git a/util-linux/Config.in b/util-linux/Config.in index e97f84062..d1688e8c9 100644 --- a/util-linux/Config.in +++ b/util-linux/Config.in | |||
@@ -458,6 +458,12 @@ config READPROFILE | |||
458 | help | 458 | help |
459 | This allows you to parse /proc/profile for basic profiling. | 459 | This allows you to parse /proc/profile for basic profiling. |
460 | 460 | ||
461 | config RTCWAKE | ||
462 | bool "rtcwake" | ||
463 | default n | ||
464 | help | ||
465 | Enter a system sleep state until specified wakeup time. | ||
466 | |||
461 | config SETARCH | 467 | config SETARCH |
462 | bool "setarch" | 468 | bool "setarch" |
463 | default n | 469 | default n |
diff --git a/util-linux/Kbuild b/util-linux/Kbuild index cc1d0e05d..4a18ff21f 100644 --- a/util-linux/Kbuild +++ b/util-linux/Kbuild | |||
@@ -26,6 +26,7 @@ lib-$(CONFIG_MOUNT) +=mount.o | |||
26 | lib-$(CONFIG_PIVOT_ROOT) +=pivot_root.o | 26 | lib-$(CONFIG_PIVOT_ROOT) +=pivot_root.o |
27 | lib-$(CONFIG_RDATE) +=rdate.o | 27 | lib-$(CONFIG_RDATE) +=rdate.o |
28 | lib-$(CONFIG_READPROFILE) +=readprofile.o | 28 | lib-$(CONFIG_READPROFILE) +=readprofile.o |
29 | lib-$(CONFIG_RTCWAKE) +=rtcwake.o | ||
29 | lib-$(CONFIG_SETARCH) +=setarch.o | 30 | lib-$(CONFIG_SETARCH) +=setarch.o |
30 | lib-$(CONFIG_SWAPONOFF) +=swaponoff.o | 31 | lib-$(CONFIG_SWAPONOFF) +=swaponoff.o |
31 | lib-$(CONFIG_SWITCH_ROOT) +=switch_root.o | 32 | lib-$(CONFIG_SWITCH_ROOT) +=switch_root.o |
diff --git a/util-linux/hwclock.c b/util-linux/hwclock.c index f91379bed..b581d2604 100644 --- a/util-linux/hwclock.c +++ b/util-linux/hwclock.c | |||
@@ -10,22 +10,7 @@ | |||
10 | #include <sys/utsname.h> | 10 | #include <sys/utsname.h> |
11 | #include <getopt.h> | 11 | #include <getopt.h> |
12 | #include "libbb.h" | 12 | #include "libbb.h" |
13 | 13 | #include "rtc_.h" | |
14 | /* Copied from linux/rtc.h to eliminate the kernel dependency */ | ||
15 | struct linux_rtc_time { | ||
16 | int tm_sec; | ||
17 | int tm_min; | ||
18 | int tm_hour; | ||
19 | int tm_mday; | ||
20 | int tm_mon; | ||
21 | int tm_year; | ||
22 | int tm_wday; | ||
23 | int tm_yday; | ||
24 | int tm_isdst; | ||
25 | }; | ||
26 | |||
27 | #define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */ | ||
28 | #define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */ | ||
29 | 14 | ||
30 | #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS | 15 | #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS |
31 | # ifndef _GNU_SOURCE | 16 | # ifndef _GNU_SOURCE |
@@ -35,56 +20,22 @@ struct linux_rtc_time { | |||
35 | 20 | ||
36 | static const char *rtcname; | 21 | static const char *rtcname; |
37 | 22 | ||
38 | static int xopen_rtc(int flags) | ||
39 | { | ||
40 | int rtc; | ||
41 | |||
42 | if (!rtcname) { | ||
43 | rtc = open("/dev/rtc", flags); | ||
44 | if (rtc >= 0) | ||
45 | return rtc; | ||
46 | rtc = open("/dev/rtc0", flags); | ||
47 | if (rtc >= 0) | ||
48 | return rtc; | ||
49 | rtcname = "/dev/misc/rtc"; | ||
50 | } | ||
51 | return xopen(rtcname, flags); | ||
52 | } | ||
53 | |||
54 | static time_t read_rtc(int utc) | 23 | static time_t read_rtc(int utc) |
55 | { | 24 | { |
56 | struct tm tm; | 25 | time_t ret; |
57 | char *oldtz = 0; | 26 | int fd; |
58 | time_t t = 0; | ||
59 | int rtc = xopen_rtc(O_RDONLY); | ||
60 | 27 | ||
61 | memset(&tm, 0, sizeof(struct tm)); | 28 | fd = rtc_xopen(rtcname, O_RDONLY); |
62 | xioctl(rtc, RTC_RD_TIME, &tm); | 29 | ret = rtc_read_time(fd, utc); |
63 | tm.tm_isdst = -1; /* not known */ | 30 | close(fd); |
64 | 31 | ||
65 | close(rtc); | 32 | return ret; |
66 | |||
67 | if (utc) { | ||
68 | oldtz = getenv("TZ"); | ||
69 | putenv((char*)"TZ=UTC0"); | ||
70 | tzset(); | ||
71 | } | ||
72 | |||
73 | t = mktime(&tm); | ||
74 | |||
75 | if (utc) { | ||
76 | unsetenv("TZ"); | ||
77 | if (oldtz) | ||
78 | putenv(oldtz - 3); | ||
79 | tzset(); | ||
80 | } | ||
81 | return t; | ||
82 | } | 33 | } |
83 | 34 | ||
84 | static void write_rtc(time_t t, int utc) | 35 | static void write_rtc(time_t t, int utc) |
85 | { | 36 | { |
86 | struct tm tm; | 37 | struct tm tm; |
87 | int rtc = xopen_rtc(O_WRONLY); | 38 | int rtc = rtc_xopen(rtcname, O_WRONLY); |
88 | 39 | ||
89 | tm = *(utc ? gmtime(&t) : localtime(&t)); | 40 | tm = *(utc ? gmtime(&t) : localtime(&t)); |
90 | tm.tm_isdst = 0; | 41 | tm.tm_isdst = 0; |
@@ -132,38 +83,6 @@ static void from_sys_clock(int utc) | |||
132 | write_rtc(tv.tv_sec, utc); | 83 | write_rtc(tv.tv_sec, utc); |
133 | } | 84 | } |
134 | 85 | ||
135 | #if ENABLE_FEATURE_HWCLOCK_ADJTIME_FHS | ||
136 | # define ADJTIME_PATH "/var/lib/hwclock/adjtime" | ||
137 | #else | ||
138 | # define ADJTIME_PATH "/etc/adjtime" | ||
139 | #endif | ||
140 | static int check_utc(void) | ||
141 | { | ||
142 | int utc = 0; | ||
143 | FILE *f = fopen(ADJTIME_PATH, "r"); | ||
144 | |||
145 | if (f) { | ||
146 | RESERVE_CONFIG_BUFFER(buffer, 128); | ||
147 | |||
148 | while (fgets(buffer, sizeof(buffer), f)) { | ||
149 | int len = strlen(buffer); | ||
150 | |||
151 | while (len && isspace(buffer[len - 1])) | ||
152 | len--; | ||
153 | |||
154 | buffer[len] = 0; | ||
155 | |||
156 | if (strncmp(buffer, "UTC", 3) == 0) { | ||
157 | utc = 1; | ||
158 | break; | ||
159 | } | ||
160 | } | ||
161 | fclose(f); | ||
162 | RELEASE_CONFIG_BUFFER(buffer); | ||
163 | } | ||
164 | return utc; | ||
165 | } | ||
166 | |||
167 | #define HWCLOCK_OPT_LOCALTIME 0x01 | 86 | #define HWCLOCK_OPT_LOCALTIME 0x01 |
168 | #define HWCLOCK_OPT_UTC 0x02 | 87 | #define HWCLOCK_OPT_UTC 0x02 |
169 | #define HWCLOCK_OPT_SHOW 0x04 | 88 | #define HWCLOCK_OPT_SHOW 0x04 |
@@ -193,19 +112,17 @@ int hwclock_main(int argc, char **argv) | |||
193 | 112 | ||
194 | /* If -u or -l wasn't given check if we are using utc */ | 113 | /* If -u or -l wasn't given check if we are using utc */ |
195 | if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) | 114 | if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) |
196 | utc = opt & HWCLOCK_OPT_UTC; | 115 | utc = (opt & HWCLOCK_OPT_UTC); |
197 | else | 116 | else |
198 | utc = check_utc(); | 117 | utc = rtc_adjtime_is_utc(); |
199 | 118 | ||
200 | if (opt & HWCLOCK_OPT_HCTOSYS) { | 119 | if (opt & HWCLOCK_OPT_HCTOSYS) |
201 | to_sys_clock(utc); | 120 | to_sys_clock(utc); |
202 | return 0; | 121 | else if (opt & HWCLOCK_OPT_SYSTOHC) |
203 | } | ||
204 | if (opt & HWCLOCK_OPT_SYSTOHC) { | ||
205 | from_sys_clock(utc); | 122 | from_sys_clock(utc); |
206 | return 0; | 123 | else |
207 | } | 124 | /* default HWCLOCK_OPT_SHOW */ |
208 | /* default HWCLOCK_OPT_SHOW */ | 125 | show_clock(utc); |
209 | show_clock(utc); | 126 | |
210 | return 0; | 127 | return 0; |
211 | } | 128 | } |
diff --git a/util-linux/rtcwake.c b/util-linux/rtcwake.c new file mode 100644 index 000000000..718f43d45 --- /dev/null +++ b/util-linux/rtcwake.c | |||
@@ -0,0 +1,209 @@ | |||
1 | /* | ||
2 | * rtcwake -- enter a system sleep state until specified wakeup time. | ||
3 | * | ||
4 | * This version was taken from util-linux and scrubbed down for busybox. | ||
5 | * | ||
6 | * This uses cross-platform Linux interfaces to enter a system sleep state, | ||
7 | * and leave it no later than a specified time. It uses any RTC framework | ||
8 | * driver that supports standard driver model wakeup flags. | ||
9 | * | ||
10 | * This is normally used like the old "apmsleep" utility, to wake from a | ||
11 | * suspend state like ACPI S1 (standby) or S3 (suspend-to-RAM). Most | ||
12 | * platforms can implement those without analogues of BIOS, APM, or ACPI. | ||
13 | * | ||
14 | * On some systems, this can also be used like "nvram-wakeup", waking | ||
15 | * from states like ACPI S4 (suspend to disk). Not all systems have | ||
16 | * persistent media that are appropriate for such suspend modes. | ||
17 | * | ||
18 | * The best way to set the system's RTC is so that it holds the current | ||
19 | * time in UTC. Use the "-l" flag to tell this program that the system | ||
20 | * RTC uses a local timezone instead (maybe you dual-boot MS-Windows). | ||
21 | * That flag should not be needed on systems with adjtime support. | ||
22 | */ | ||
23 | |||
24 | #include "libbb.h" | ||
25 | #include "rtc_.h" | ||
26 | |||
27 | #define SYS_RTC_PATH "/sys/class/rtc/%s/device/power/wakeup" | ||
28 | #define SYS_POWER_PATH "/sys/power/state" | ||
29 | #define DEFAULT_MODE "suspend" | ||
30 | |||
31 | static time_t rtc_time; | ||
32 | |||
33 | static int may_wakeup(const char *rtcname) | ||
34 | { | ||
35 | ssize_t ret; | ||
36 | char buf[128]; | ||
37 | |||
38 | /* strip the '/dev/' from the rtcname here */ | ||
39 | if (!strncmp(rtcname, "/dev/", 5)) | ||
40 | rtcname += 5; | ||
41 | |||
42 | snprintf(buf, sizeof(buf), SYS_RTC_PATH, rtcname); | ||
43 | ret = open_read_close(buf, buf, sizeof(buf)); | ||
44 | if (ret < 0) | ||
45 | return 0; | ||
46 | |||
47 | /* wakeup events could be disabled or not supported */ | ||
48 | return strcmp(buf, "enabled\n") == 0; | ||
49 | } | ||
50 | |||
51 | static void setup_alarm(int fd, time_t *wakeup) | ||
52 | { | ||
53 | struct tm *tm; | ||
54 | struct linux_rtc_wkalrm wake; | ||
55 | |||
56 | /* The wakeup time is in POSIX time (more or less UTC). | ||
57 | * Ideally RTCs use that same time; but PCs can't do that | ||
58 | * if they need to boot MS-Windows. Messy... | ||
59 | * | ||
60 | * When running in utc mode this process's timezone is UTC, | ||
61 | * so we'll pass a UTC date to the RTC. | ||
62 | * | ||
63 | * Else mode is local so the time given to the RTC | ||
64 | * will instead use the local time zone. | ||
65 | */ | ||
66 | tm = localtime(wakeup); | ||
67 | |||
68 | wake.time.tm_sec = tm->tm_sec; | ||
69 | wake.time.tm_min = tm->tm_min; | ||
70 | wake.time.tm_hour = tm->tm_hour; | ||
71 | wake.time.tm_mday = tm->tm_mday; | ||
72 | wake.time.tm_mon = tm->tm_mon; | ||
73 | wake.time.tm_year = tm->tm_year; | ||
74 | /* wday, yday, and isdst fields are unused by Linux */ | ||
75 | wake.time.tm_wday = -1; | ||
76 | wake.time.tm_yday = -1; | ||
77 | wake.time.tm_isdst = -1; | ||
78 | |||
79 | /* many rtc alarms only support up to 24 hours from 'now', | ||
80 | * so use the "more than 24 hours" request only if we must | ||
81 | */ | ||
82 | if ((rtc_time + (24 * 60 * 60)) > *wakeup) { | ||
83 | xioctl(fd, RTC_ALM_SET, &wake.time); | ||
84 | xioctl(fd, RTC_AIE_ON, 0); | ||
85 | } else { | ||
86 | /* avoid an extra AIE_ON call */ | ||
87 | wake.enabled = 1; | ||
88 | xioctl(fd, RTC_WKALM_SET, &wake); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | static void suspend_system(const char *suspend) | ||
93 | { | ||
94 | FILE *f = xfopen(SYS_POWER_PATH, "w"); | ||
95 | fprintf(f, "%s\n", suspend); | ||
96 | fflush(f); | ||
97 | /* this executes after wake from suspend */ | ||
98 | fclose(f); | ||
99 | } | ||
100 | |||
101 | #define RTCWAKE_OPT_AUTO 0x01 | ||
102 | #define RTCWAKE_OPT_LOCAL 0x02 | ||
103 | #define RTCWAKE_OPT_UTC 0x04 | ||
104 | #define RTCWAKE_OPT_DEVICE 0x08 | ||
105 | #define RTCWAKE_OPT_SUSPEND_MODE 0x10 | ||
106 | #define RTCWAKE_OPT_SECONDS 0x20 | ||
107 | #define RTCWAKE_OPT_TIME 0x40 | ||
108 | |||
109 | int rtcwake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
110 | int rtcwake_main(int argc, char **argv) | ||
111 | { | ||
112 | unsigned opt; | ||
113 | const char *rtcname = NULL; | ||
114 | const char *suspend; | ||
115 | const char *opt_seconds; | ||
116 | const char *opt_time; | ||
117 | |||
118 | time_t sys_time; | ||
119 | time_t alarm_time = 0; | ||
120 | unsigned seconds = 0; | ||
121 | int utc = -1; | ||
122 | int fd; | ||
123 | |||
124 | #if ENABLE_GETOPT_LONG | ||
125 | static const char rtcwake_longopts[] ALIGN1 = | ||
126 | "auto\0" No_argument "a" | ||
127 | "local\0" No_argument "l" | ||
128 | "utc\0" No_argument "u" | ||
129 | "device\0" Required_argument "d" | ||
130 | "mode\0" Required_argument "m" | ||
131 | "seconds\0" Required_argument "s" | ||
132 | "time\0" Required_argument "t" | ||
133 | ; | ||
134 | applet_long_options = rtcwake_longopts; | ||
135 | } | ||
136 | #endif | ||
137 | opt = getopt32(argv, "alud:m:s:t:", &rtcname, &suspend, &opt_seconds, &opt_time); | ||
138 | |||
139 | /* this is the default | ||
140 | if (opt & RTCWAKE_OPT_AUTO) | ||
141 | utc = -1; | ||
142 | */ | ||
143 | if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL)) | ||
144 | utc = opt & RTCWAKE_OPT_UTC; | ||
145 | if (!(opt & RTCWAKE_OPT_SUSPEND_MODE)) | ||
146 | suspend = DEFAULT_MODE; | ||
147 | if (opt & RTCWAKE_OPT_SECONDS) | ||
148 | /* alarm time, seconds-to-sleep (relative) */ | ||
149 | seconds = xatoi(opt_seconds); | ||
150 | if (opt & RTCWAKE_OPT_TIME) | ||
151 | /* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */ | ||
152 | alarm_time = xatoi(opt_time); | ||
153 | |||
154 | if (!alarm_time && !seconds) | ||
155 | bb_error_msg_and_die("must provide wake time"); | ||
156 | |||
157 | if (utc == -1) | ||
158 | utc = rtc_adjtime_is_utc(); | ||
159 | |||
160 | /* the rtcname is relative to /dev */ | ||
161 | xchdir("/dev"); | ||
162 | |||
163 | if (strcmp(suspend, "on") != 0 && !may_wakeup(rtcname)) | ||
164 | bb_error_msg_and_die("%s not enabled for wakeup events", rtcname); | ||
165 | |||
166 | /* this RTC must exist and (if we'll sleep) be wakeup-enabled */ | ||
167 | fd = rtc_xopen(rtcname, O_RDONLY); | ||
168 | |||
169 | /* relative or absolute alarm time, normalized to time_t */ | ||
170 | sys_time = time(0); | ||
171 | if (sys_time == (time_t)-1) | ||
172 | bb_perror_msg_and_die("read system time"); | ||
173 | rtc_time = rtc_read_time(fd, utc); | ||
174 | |||
175 | if (alarm_time) { | ||
176 | if (alarm_time < sys_time) | ||
177 | bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time)); | ||
178 | alarm_time += sys_time - rtc_time; | ||
179 | } else | ||
180 | alarm_time = rtc_time + seconds + 1; | ||
181 | setup_alarm(fd, &alarm_time); | ||
182 | |||
183 | sync(); | ||
184 | printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time)); | ||
185 | fflush(stdout); | ||
186 | usleep(10 * 1000); | ||
187 | |||
188 | if (!strcmp(suspend, "on")) | ||
189 | suspend_system(suspend); | ||
190 | else { | ||
191 | /* "fake" suspend ... we'll do the delay ourselves */ | ||
192 | unsigned long data; | ||
193 | |||
194 | do { | ||
195 | ssize_t ret = safe_read(fd, &data, sizeof(data)); | ||
196 | if (ret < 0) { | ||
197 | bb_perror_msg("rtc read"); | ||
198 | break; | ||
199 | } | ||
200 | } while (!(data & RTC_AF)); | ||
201 | } | ||
202 | |||
203 | xioctl(fd, RTC_AIE_OFF, 0); | ||
204 | |||
205 | if (ENABLE_FEATURE_CLEAN_UP) | ||
206 | close(fd); | ||
207 | |||
208 | return EXIT_SUCCESS; | ||
209 | } | ||