source: MondoRescue/branches/3.3/mindi-busybox/util-linux/rtcwake.c@ 3865

Last change on this file since 3865 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 7.1 KB
Line 
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 * Licensed under GPLv2, see file LICENSE in this source tree.
7 *
8 * This uses cross-platform Linux interfaces to enter a system sleep state,
9 * and leave it no later than a specified time. It uses any RTC framework
10 * driver that supports standard driver model wakeup flags.
11 *
12 * This is normally used like the old "apmsleep" utility, to wake from a
13 * suspend state like ACPI S1 (standby) or S3 (suspend-to-RAM). Most
14 * platforms can implement those without analogues of BIOS, APM, or ACPI.
15 *
16 * On some systems, this can also be used like "nvram-wakeup", waking
17 * from states like ACPI S4 (suspend to disk). Not all systems have
18 * persistent media that are appropriate for such suspend modes.
19 *
20 * The best way to set the system's RTC is so that it holds the current
21 * time in UTC. Use the "-l" flag to tell this program that the system
22 * RTC uses a local timezone instead (maybe you dual-boot MS-Windows).
23 * That flag should not be needed on systems with adjtime support.
24 */
25
26//usage:#define rtcwake_trivial_usage
27//usage: "[-a | -l | -u] [-d DEV] [-m MODE] [-s SEC | -t TIME]"
28//usage:#define rtcwake_full_usage "\n\n"
29//usage: "Enter a system sleep state until specified wakeup time\n"
30//usage: IF_LONG_OPTS(
31//usage: "\n -a,--auto Read clock mode from adjtime"
32//usage: "\n -l,--local Clock is set to local time"
33//usage: "\n -u,--utc Clock is set to UTC time"
34//usage: "\n -d,--device=DEV Specify the RTC device"
35//usage: "\n -m,--mode=MODE Set sleep state (default: standby)"
36//usage: "\n -s,--seconds=SEC Set timeout in SEC seconds from now"
37//usage: "\n -t,--time=TIME Set timeout to TIME seconds from epoch"
38//usage: )
39//usage: IF_NOT_LONG_OPTS(
40//usage: "\n -a Read clock mode from adjtime"
41//usage: "\n -l Clock is set to local time"
42//usage: "\n -u Clock is set to UTC time"
43//usage: "\n -d DEV Specify the RTC device"
44//usage: "\n -m MODE Set sleep state (default: standby)"
45//usage: "\n -s SEC Set timeout in SEC seconds from now"
46//usage: "\n -t TIME Set timeout to TIME seconds from epoch"
47//usage: )
48
49#include "libbb.h"
50#include "rtc_.h"
51
52#define SYS_RTC_PATH "/sys/class/rtc/%s/device/power/wakeup"
53#define SYS_POWER_PATH "/sys/power/state"
54
55static NOINLINE bool may_wakeup(const char *rtcname)
56{
57 ssize_t ret;
58 char buf[128];
59
60 /* strip "/dev/" from the rtcname here */
61 rtcname = skip_dev_pfx(rtcname);
62
63 snprintf(buf, sizeof(buf), SYS_RTC_PATH, rtcname);
64 ret = open_read_close(buf, buf, sizeof(buf));
65 if (ret < 0)
66 return false;
67
68 /* wakeup events could be disabled or not supported */
69 return is_prefixed_with(buf, "enabled\n") != NULL;
70}
71
72static NOINLINE void setup_alarm(int fd, time_t *wakeup, time_t rtc_time)
73{
74 struct tm *ptm;
75 struct linux_rtc_wkalrm wake;
76
77 /* The wakeup time is in POSIX time (more or less UTC).
78 * Ideally RTCs use that same time; but PCs can't do that
79 * if they need to boot MS-Windows. Messy...
80 *
81 * When running in utc mode this process's timezone is UTC,
82 * so we'll pass a UTC date to the RTC.
83 *
84 * Else mode is local so the time given to the RTC
85 * will instead use the local time zone.
86 */
87 ptm = localtime(wakeup);
88
89 wake.time.tm_sec = ptm->tm_sec;
90 wake.time.tm_min = ptm->tm_min;
91 wake.time.tm_hour = ptm->tm_hour;
92 wake.time.tm_mday = ptm->tm_mday;
93 wake.time.tm_mon = ptm->tm_mon;
94 wake.time.tm_year = ptm->tm_year;
95 /* wday, yday, and isdst fields are unused by Linux */
96 wake.time.tm_wday = -1;
97 wake.time.tm_yday = -1;
98 wake.time.tm_isdst = -1;
99
100 /* many rtc alarms only support up to 24 hours from 'now',
101 * so use the "more than 24 hours" request only if we must
102 */
103 if ((rtc_time + (24 * 60 * 60)) > *wakeup) {
104 xioctl(fd, RTC_ALM_SET, &wake.time);
105 xioctl(fd, RTC_AIE_ON, 0);
106 } else {
107 /* avoid an extra AIE_ON call */
108 wake.enabled = 1;
109 xioctl(fd, RTC_WKALM_SET, &wake);
110 }
111}
112
113#define RTCWAKE_OPT_AUTO 0x01
114#define RTCWAKE_OPT_LOCAL 0x02
115#define RTCWAKE_OPT_UTC 0x04
116#define RTCWAKE_OPT_DEVICE 0x08
117#define RTCWAKE_OPT_SUSPEND_MODE 0x10
118#define RTCWAKE_OPT_SECONDS 0x20
119#define RTCWAKE_OPT_TIME 0x40
120
121int rtcwake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
122int rtcwake_main(int argc UNUSED_PARAM, char **argv)
123{
124 unsigned opt;
125 const char *rtcname = NULL;
126 const char *suspend = "standby";
127 const char *opt_seconds;
128 const char *opt_time;
129
130 time_t rtc_time;
131 time_t sys_time;
132 time_t alarm_time = alarm_time;
133 unsigned seconds = seconds; /* for compiler */
134 int utc = -1;
135 int fd;
136
137#if ENABLE_LONG_OPTS
138 static const char rtcwake_longopts[] ALIGN1 =
139 "auto\0" No_argument "a"
140 "local\0" No_argument "l"
141 "utc\0" No_argument "u"
142 "device\0" Required_argument "d"
143 "mode\0" Required_argument "m"
144 "seconds\0" Required_argument "s"
145 "time\0" Required_argument "t"
146 ;
147 applet_long_options = rtcwake_longopts;
148#endif
149 /* Must have -s or -t, exclusive */
150 opt_complementary = "s:t:s--t:t--s";
151 opt = getopt32(argv, "alud:m:s:t:", &rtcname, &suspend, &opt_seconds, &opt_time);
152
153 /* this is the default
154 if (opt & RTCWAKE_OPT_AUTO)
155 utc = -1;
156 */
157 if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL))
158 utc = opt & RTCWAKE_OPT_UTC;
159 if (opt & RTCWAKE_OPT_SECONDS) {
160 /* alarm time, seconds-to-sleep (relative) */
161 seconds = xatou(opt_seconds);
162 } else {
163 /* RTCWAKE_OPT_TIME */
164 /* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */
165 if (sizeof(alarm_time) <= sizeof(long))
166 alarm_time = xatol(opt_time);
167 else
168 alarm_time = xatoll(opt_time);
169 }
170
171 if (utc == -1)
172 utc = rtc_adjtime_is_utc();
173
174 /* the rtcname is relative to /dev */
175 xchdir("/dev");
176
177 /* this RTC must exist and (if we'll sleep) be wakeup-enabled */
178 fd = rtc_xopen(&rtcname, O_RDONLY);
179
180 if (strcmp(suspend, "on") != 0)
181 if (!may_wakeup(rtcname))
182 bb_error_msg_and_die("%s not enabled for wakeup events", rtcname);
183
184 /* relative or absolute alarm time, normalized to time_t */
185 sys_time = time(NULL);
186 {
187 struct tm tm_time;
188 rtc_read_tm(&tm_time, fd);
189 rtc_time = rtc_tm2time(&tm_time, utc);
190 }
191
192 if (opt & RTCWAKE_OPT_TIME) {
193 /* Correct for RTC<->system clock difference */
194 alarm_time += rtc_time - sys_time;
195 if (alarm_time < rtc_time)
196 /*
197 * Compat message text.
198 * I'd say "RTC time is already ahead of ..." instead.
199 */
200 bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time));
201 } else
202 alarm_time = rtc_time + seconds + 1;
203
204 setup_alarm(fd, &alarm_time, rtc_time);
205 sync();
206#if 0 /*debug*/
207 printf("sys_time: %s", ctime(&sys_time));
208 printf("rtc_time: %s", ctime(&rtc_time));
209#endif
210 printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time));
211 fflush_all();
212 usleep(10 * 1000);
213
214 if (strcmp(suspend, "on") != 0)
215 xopen_xwrite_close(SYS_POWER_PATH, suspend);
216 else {
217 /* "fake" suspend ... we'll do the delay ourselves */
218 unsigned long data;
219
220 do {
221 ssize_t ret = safe_read(fd, &data, sizeof(data));
222 if (ret < 0) {
223 bb_perror_msg("rtc read");
224 break;
225 }
226 } while (!(data & RTC_AF));
227 }
228
229 xioctl(fd, RTC_AIE_OFF, 0);
230
231 if (ENABLE_FEATURE_CLEAN_UP)
232 close(fd);
233
234 return EXIT_SUCCESS;
235}
Note: See TracBrowser for help on using the repository browser.