1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
/* rtcwake.c - enter sleep state until given time.
*
* Copyright 2020 The Android Open Source Project
USE_RTCWAKE(NEWTOY(rtcwake, "(list-modes);(auto)a(device)d:(local)l(mode)m:(seconds)s#(time)t#(utc)u(verbose)v[!alu]", TOYFLAG_USR|TOYFLAG_BIN))
config RTCWAKE
bool "rtcwake"
default n
help
usage: rtcwake [-aluv] [-d FILE] [-m MODE] [-s SECS] [-t UNIX]
Enter the given sleep state until the given time.
-a RTC uses time specified in /etc/adjtime
-d FILE Device to use (default /dev/rtc)
-l RTC uses local time
-m Mode (--list-modes to see those supported by your kernel):
disable Cancel current alarm
freeze Freeze processes, idle processors
disk S4: suspend to disk
mem S3: suspend to RAM
no Don't suspend, just set wakeup time
off S5: power off
on Don't suspend, poll RTC for alarm
show Don't suspend, just show current alarm
standby S1: default
-s SECS Wake SECS seconds from now
-t UNIX Wake UNIX seconds from epoch
-u RTC uses UTC
-v Verbose
*/
#define FOR_rtcwake
#include "toys.h"
#include <linux/rtc.h>
GLOBALS(
long long t, s;
char *m, *d;
)
void rtcwake_main(void)
{
struct rtc_wkalrm alarm;
struct tm rtc_tm;
time_t now, rtc_now, then;
int fd, utc;
memset(&alarm, 0, sizeof(alarm));
if (FLAG(list_modes)) {
xreadfile("/sys/power/state", toybuf, sizeof(toybuf));
printf("off no on disable show %s", toybuf);
return;
}
// util-linux defaults to "suspend", even though I don't have anything that
// supports that (testing everything from a ~2010 laptop to a 2019 desktop).
if (!TT.m) TT.m = "suspend";
if (FLAG(u)) utc = 1;
else if (FLAG(l)) utc = 0;
else {
xreadfile("/etc/adjtime", toybuf, sizeof(toybuf));
utc = !!strstr(toybuf, "UTC");
}
if (FLAG(v)) xprintf("RTC time: %s\n", utc ? "UTC" : "local");
if (!TT.d) TT.d = "/dev/rtc0";
if (FLAG(v)) xprintf("Device: %s\n", TT.d);
fd = xopen(TT.d, O_RDWR);
now = time(0);
xioctl(fd, RTC_RD_TIME, &rtc_tm);
rtc_now = xmktime(&rtc_tm, utc);
if (FLAG(v)) {
xprintf("System time:\t%lld / %s", (long long)now, ctime(&now));
xprintf("RTC time:\t%lld / %s", (long long)rtc_now, ctime(&rtc_now));
}
if (!strcmp(TT.m, "show")) { // Don't suspend, just show current alarm.
xioctl(fd, RTC_WKALM_RD, &alarm);
if (!alarm.enabled) xputs("alarm: off");
else {
if ((then = mktime((void *)&alarm.time)) < 0) perror_exit("mktime");
xprintf("alarm: on %s", ctime(&then));
}
goto done;
} else if (!strcmp(TT.m, "disable")) { // Cancel current alarm.
xioctl(fd, RTC_WKALM_RD, &alarm);
alarm.enabled = 0;
xioctl(fd, RTC_WKALM_SET, &alarm);
goto done;
}
if (FLAG(s)) {
then = rtc_now + TT.s + 1; // strace shows util-linux adds 1.
} else if (FLAG(t)) {
then = TT.t + (rtc_now - now);
if (then<=rtc_now) error_exit("rtc %lld >= %lld", (long long)rtc_now, TT.t);
} else help_exit("-m %s needs -s or -t", TT.m);
if (FLAG(v)) xprintf("Wake time:\t%lld / %s", (long long)then, ctime(&then));
if (!(utc ? gmtime_r : localtime_r)(&then, (void *)&alarm.time))
error_exit(utc ? "gmtime_r failed" : "localtime_r failed");
alarm.enabled = 1;
xioctl(fd, RTC_WKALM_SET, &alarm);
sync();
xprintf("wakeup using \"%s\" from %s at %s", TT.m, TT.d, ctime(&then));
msleep(10);
if (!strcmp(TT.m, "no")) { // Don't suspend, just set wakeup time.
} else if (!strcmp(TT.m, "on")) { // Don't suspend, poll RTC for alarm.
unsigned long data = 0;
if (FLAG(v)) xputs("Reading RTC...");
while (!(data & RTC_AF)) {
if (read(fd, &data, sizeof(data)) != sizeof(data)) perror_exit("read");
if (FLAG(v)) xprintf("... %s: %lx\n", TT.d, data);
}
} else if (!strcmp(TT.m, "off")) {
xexec((char *[]){"poweroff", 0});
} else {
// Everything else lands here for one final step. The write will fail with
// EINVAL if the mode is not supported.
int fd = xopen("/sys/power/state", O_WRONLY);
xwrite(fd, TT.m, strlen(TT.m));
close(fd);
}
done:
close(fd);
}
|