2 * rtcwake -- enter a system sleep state until specified wakeup time.
4 * This uses cross-platform Linux interfaces to enter a system sleep state,
5 * and leave it no later than a specified time. It uses any RTC framework
6 * driver that supports standard driver model wakeup flags.
8 * This is normally used like the old "apmsleep" utility, to wake from a
9 * suspend state like ACPI S1 (standby) or S3 (suspend-to-RAM). Most
10 * platforms can implement those without analogues of BIOS, APM, or ACPI.
12 * On some systems, this can also be used like "nvram-wakeup", waking
13 * from states like ACPI S4 (suspend to disk). Not all systems have
14 * persistent media that are appropriate for such suspend modes.
16 * The best way to set the system's RTC is so that it holds the current
17 * time in UTC. Use the "-l" flag to tell this program that the system
18 * RTC uses a local timezone instead (maybe you dual-boot MS-Windows).
19 * That flag should not be needed on systems with adjtime support.
25 #include <linux/rtc.h>
29 #include <sys/ioctl.h>
31 #include <sys/types.h>
36 #include "closestream.h"
39 #include "pathnames.h"
42 #include "timeutils.h"
46 # define RTC_AF 0x20 /* Alarm interrupt */
49 #define ADJTIME_ZONE_BUFSIZ 8
50 #define SYS_WAKEUP_PATH_TEMPLATE "/sys/class/rtc/%s/device/power/wakeup"
51 #define SYS_POWER_STATE_PATH "/sys/power/state"
52 #define DEFAULT_RTC_DEVICE "/dev/rtc0"
54 enum rtc_modes
{ /* manual page --mode option explains these. */
61 SYSFS_MODE
/* keep it last */
65 static const char *rtcwake_mode_string
[] = {
69 [DISABLE_MODE
] = "disable",
79 struct rtcwake_control
{
80 char *mode_str
; /* name of the requested mode */
81 char **possible_modes
; /* modes listed in /sys/power/state */
82 char *adjfile
; /* adjtime file path */
83 enum clock_modes clock_mode
; /* hwclock timezone */
84 time_t sys_time
; /* system time */
85 time_t rtc_time
; /* hardware time */
86 unsigned int verbose
:1, /* verbose messaging */
87 dryrun
:1; /* do not set alarm, suspend system, etc */
90 static void __attribute__((__noreturn__
)) usage(FILE *out
)
92 fputs(USAGE_HEADER
, out
);
94 _(" %s [options]\n"), program_invocation_short_name
);
96 fputs(USAGE_SEPARATOR
, out
);
97 fputs(_("Enter a system sleep state until a specified wakeup time.\n"), out
);
99 fputs(USAGE_OPTIONS
, out
);
100 fputs(_(" -a, --auto reads the clock mode from adjust file (default)\n"), out
);
102 _(" -A, --adjfile <file> specifies the path to the adjust file\n"
103 " the default is %s\n"), _PATH_ADJTIME
);
104 fputs(_(" --date <timestamp> date time of timestamp to wake\n"), out
);
105 fputs(_(" -d, --device <device> select rtc device (rtc0|rtc1|...)\n"), out
);
106 fputs(_(" -n, --dry-run does everything, but suspend\n"), out
);
107 fputs(_(" -l, --local RTC uses local timezone\n"), out
);
108 fputs(_(" --list-modes list available modes\n"), out
);
109 fputs(_(" -m, --mode <mode> standby|mem|... sleep mode\n"), out
);
110 fputs(_(" -s, --seconds <seconds> seconds to sleep\n"), out
);
111 fputs(_(" -t, --time <time_t> time to wake\n"), out
);
112 fputs(_(" -u, --utc RTC uses UTC\n"), out
);
113 fputs(_(" -v, --verbose verbose messages\n"), out
);
115 printf(USAGE_SEPARATOR
);
117 printf(USAGE_VERSION
);
119 printf(USAGE_MAN_TAIL("rtcwake(8)"));
121 exit(out
== stderr
? EXIT_FAILURE
: EXIT_SUCCESS
);
124 static int is_wakeup_enabled(const char *devname
)
130 if (startswith(devname
, "/dev/"))
132 snprintf(buf
, sizeof buf
, SYS_WAKEUP_PATH_TEMPLATE
, devname
+ skip
);
135 warn(_("cannot open %s"), buf
);
139 s
= fgets(buf
, sizeof buf
, f
);
143 s
= strchr(buf
, '\n');
147 /* wakeup events could be disabled or not supported */
148 return strcmp(buf
, "enabled") == 0;
151 static int get_basetimes(struct rtcwake_control
*ctl
, int fd
)
153 struct tm tm
= { 0 };
156 /* This process works in RTC time, except when working
157 * with the system clock (which always uses UTC).
159 if (ctl
->clock_mode
== CM_UTC
)
160 setenv("TZ", "UTC", 1);
162 /* Read rtc and system clocks "at the same time", or as
163 * precisely (+/- a second) as we can read them.
165 if (ioctl(fd
, RTC_RD_TIME
, &rtc
) < 0) {
166 warn(_("read rtc time failed"));
170 ctl
->sys_time
= time(0);
171 if (ctl
->sys_time
== (time_t)-1) {
172 warn(_("read system time failed"));
175 /* Convert rtc_time to normal arithmetic-friendly form,
176 * updating tm.tm_wday as used by asctime().
178 tm
.tm_sec
= rtc
.tm_sec
;
179 tm
.tm_min
= rtc
.tm_min
;
180 tm
.tm_hour
= rtc
.tm_hour
;
181 tm
.tm_mday
= rtc
.tm_mday
;
182 tm
.tm_mon
= rtc
.tm_mon
;
183 tm
.tm_year
= rtc
.tm_year
;
184 tm
.tm_isdst
= -1; /* assume the system knows better than the RTC */
186 ctl
->rtc_time
= mktime(&tm
);
187 if (ctl
->rtc_time
== (time_t)-1) {
188 warn(_("convert rtc time failed"));
193 /* Unless the system uses UTC, either delta or tzone
194 * reflects a seconds offset from UTC. The value can
195 * help sort out problems like bugs in your C library. */
196 printf("\tdelta = %ld\n", ctl
->sys_time
- ctl
->rtc_time
);
197 printf("\ttzone = %ld\n", timezone
);
198 printf("\ttzname = %s\n", tzname
[daylight
]);
199 gmtime_r(&ctl
->rtc_time
, &tm
);
200 printf("\tsystime = %ld, (UTC) %s",
201 (long) ctl
->sys_time
, asctime(gmtime(&ctl
->sys_time
)));
202 printf("\trtctime = %ld, (UTC) %s",
203 (long) ctl
->rtc_time
, asctime(&tm
));
208 static int setup_alarm(struct rtcwake_control
*ctl
, int fd
, time_t *wakeup
)
211 struct rtc_wkalrm wake
= { 0 };
213 /* The wakeup time is in POSIX time (more or less UTC). Ideally
214 * RTCs use that same time; but PCs can't do that if they need to
215 * boot MS-Windows. Messy...
217 * When clock_mode == CM_UTC this process's timezone is UTC, so
218 * we'll pass a UTC date to the RTC.
220 * Else clock_mode == CM_LOCAL so the time given to the RTC will
221 * instead use the local time zone. */
222 tm
= localtime(wakeup
);
223 wake
.time
.tm_sec
= tm
->tm_sec
;
224 wake
.time
.tm_min
= tm
->tm_min
;
225 wake
.time
.tm_hour
= tm
->tm_hour
;
226 wake
.time
.tm_mday
= tm
->tm_mday
;
227 wake
.time
.tm_mon
= tm
->tm_mon
;
228 wake
.time
.tm_year
= tm
->tm_year
;
229 /* wday, yday, and isdst fields are unused */
230 wake
.time
.tm_wday
= -1;
231 wake
.time
.tm_yday
= -1;
232 wake
.time
.tm_isdst
= -1;
235 if (!ctl
->dryrun
&& ioctl(fd
, RTC_WKALM_SET
, &wake
) < 0) {
236 warn(_("set rtc wake alarm failed"));
242 static char **get_sys_power_states(struct rtcwake_control
*ctl
)
246 if (!ctl
->possible_modes
) {
247 char buf
[256] = { 0 };
249 fd
= open(SYS_POWER_STATE_PATH
, O_RDONLY
);
252 if (read(fd
, &buf
, sizeof buf
) <= 0)
254 ctl
->possible_modes
= strv_split(buf
, " \n");
257 return ctl
->possible_modes
;
264 static void suspend_system(struct rtcwake_control
*ctl
)
266 FILE *f
= fopen(SYS_POWER_STATE_PATH
, "w");
269 warn(_("cannot open %s"), SYS_POWER_STATE_PATH
);
274 fprintf(f
, "%s\n", ctl
->mode_str
);
277 /* this executes after wake from suspend */
279 errx(EXIT_FAILURE
, _("write error"));
282 static int read_clock_mode(struct rtcwake_control
*ctl
)
285 char linebuf
[ADJTIME_ZONE_BUFSIZ
];
287 fp
= fopen(ctl
->adjfile
, "r");
291 if (skip_fline(fp
) || skip_fline(fp
)) {
295 /* read third line */
296 if (!fgets(linebuf
, sizeof linebuf
, fp
)) {
301 if (strncmp(linebuf
, "UTC", 3) == 0)
302 ctl
->clock_mode
= CM_UTC
;
303 else if (strncmp(linebuf
, "LOCAL", 5) == 0)
304 ctl
->clock_mode
= CM_LOCAL
;
305 else if (ctl
->verbose
)
306 warnx(_("unexpected third line in: %s: %s"), ctl
->adjfile
, linebuf
);
312 static int print_alarm(struct rtcwake_control
*ctl
, int fd
)
314 struct rtc_wkalrm wake
;
315 struct tm tm
= { 0 };
318 if (ioctl(fd
, RTC_WKALM_RD
, &wake
) < 0) {
319 warn(_("read rtc alarm failed"));
323 if (wake
.enabled
!= 1 || wake
.time
.tm_year
== -1) {
324 printf(_("alarm: off\n"));
327 tm
.tm_sec
= wake
.time
.tm_sec
;
328 tm
.tm_min
= wake
.time
.tm_min
;
329 tm
.tm_hour
= wake
.time
.tm_hour
;
330 tm
.tm_mday
= wake
.time
.tm_mday
;
331 tm
.tm_mon
= wake
.time
.tm_mon
;
332 tm
.tm_year
= wake
.time
.tm_year
;
333 tm
.tm_isdst
= -1; /* assume the system knows better than the RTC */
336 if (alarm
== (time_t)-1) {
337 warn(_("convert time failed"));
340 /* 0 if both UTC, or expresses diff if RTC in local time */
341 alarm
+= ctl
->sys_time
- ctl
->rtc_time
;
342 printf(_("alarm: on %s"), ctime(&alarm
));
347 static int get_rtc_mode(struct rtcwake_control
*ctl
, const char *optarg
)
350 char **modes
= get_sys_power_states(ctl
), **m
;
352 STRV_FOREACH(m
, modes
) {
353 if (strcmp(optarg
, *m
) == 0)
357 for (i
= 0; i
< ARRAY_SIZE(rtcwake_mode_string
); i
++)
358 if (!strcmp(optarg
, rtcwake_mode_string
[i
]))
364 static int open_dev_rtc(const char *devname
)
367 char *devpath
= NULL
;
369 if (startswith(devname
, "/dev"))
370 devpath
= xstrdup(devname
);
372 xasprintf(&devpath
, "/dev/%s", devname
);
373 fd
= open(devpath
, O_RDONLY
| O_CLOEXEC
);
375 err(EXIT_FAILURE
, _("%s: unable to find device"), devpath
);
380 static void list_modes(struct rtcwake_control
*ctl
)
383 char **modes
= get_sys_power_states(ctl
), **m
;
386 errx(EXIT_FAILURE
, _("could not read: %s"), SYS_POWER_STATE_PATH
);
388 STRV_FOREACH(m
, modes
)
391 for (i
= 0; i
< ARRAY_SIZE(rtcwake_mode_string
); i
++)
392 printf("%s ", rtcwake_mode_string
[i
]);
396 int main(int argc
, char **argv
)
398 struct rtcwake_control ctl
= {
399 .mode_str
= "suspend", /* default mode */
400 .adjfile
= _PATH_ADJTIME
,
401 .clock_mode
= CM_AUTO
403 char *devname
= DEFAULT_RTC_DEVICE
;
404 unsigned seconds
= 0;
405 int suspend
= SYSFS_MODE
;
406 int rc
= EXIT_SUCCESS
;
411 OPT_DATE
= CHAR_MAX
+ 1,
414 static const struct option long_options
[] = {
415 {"adjfile", required_argument
, 0, 'A'},
416 {"auto", no_argument
, 0, 'a'},
417 {"dry-run", no_argument
, 0, 'n'},
418 {"local", no_argument
, 0, 'l'},
419 {"utc", no_argument
, 0, 'u'},
420 {"verbose", no_argument
, 0, 'v'},
421 {"version", no_argument
, 0, 'V'},
422 {"help", no_argument
, 0, 'h'},
423 {"mode", required_argument
, 0, 'm'},
424 {"device", required_argument
, 0, 'd'},
425 {"seconds", required_argument
, 0, 's'},
426 {"time", required_argument
, 0, 't'},
427 {"date", required_argument
, 0, OPT_DATE
},
428 {"list-modes", no_argument
, 0, OPT_LIST
},
431 static const ul_excl_t excl
[] = {
433 { 's', 't', OPT_DATE
},
435 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
437 setlocale(LC_ALL
, "");
438 bindtextdomain(PACKAGE
, LOCALEDIR
);
440 atexit(close_stdout
);
442 while ((t
= getopt_long(argc
, argv
, "A:ahd:lm:ns:t:uVv",
443 long_options
, NULL
)) != EOF
) {
444 err_exclusive_options(t
, long_options
, excl
, excl_st
);
447 /* for better compatibility with hwclock */
448 ctl
.adjfile
= optarg
;
451 ctl
.clock_mode
= CM_AUTO
;
457 ctl
.clock_mode
= CM_LOCAL
;
465 if ((suspend
= get_rtc_mode(&ctl
, optarg
)) < 0)
466 errx(EXIT_FAILURE
, _("unrecognized suspend state '%s'"), optarg
);
467 ctl
.mode_str
= optarg
;
473 /* alarm time, seconds-to-sleep (relative) */
474 seconds
= strtou32_or_err(optarg
, _("invalid seconds argument"));
477 /* alarm time, time_t (absolute, seconds since epoc) */
478 alarm
= strtou32_or_err(optarg
, _("invalid time argument"));
481 { /* alarm time, see timestamp format from manual */
483 if (parse_timestamp(optarg
, &p
) < 0)
484 errx(EXIT_FAILURE
, _("invalid time value \"%s\""), optarg
);
485 alarm
= (time_t) (p
/ 1000000);
489 ctl
.clock_mode
= CM_UTC
;
495 printf(UTIL_LINUX_VERSION
);
504 if (ctl
.clock_mode
== CM_AUTO
) {
505 if (read_clock_mode(&ctl
) < 0) {
506 printf(_("%s: assuming RTC uses UTC ...\n"),
507 program_invocation_short_name
);
508 ctl
.clock_mode
= CM_UTC
;
513 printf("%s", ctl
.clock_mode
== CM_UTC
? _("Using UTC time.\n") :
514 _("Using local time.\n"));
516 if (!alarm
&& !seconds
&& suspend
!= DISABLE_MODE
&& suspend
!= SHOW_MODE
)
517 errx(EXIT_FAILURE
, _("must provide wake time (see --seconds, --time and --date options)"));
519 /* device must exist and (if we'll sleep) be wakeup-enabled */
520 fd
= open_dev_rtc(devname
);
522 if (suspend
!= ON_MODE
&& suspend
!= NO_MODE
&& !is_wakeup_enabled(devname
))
523 errx(EXIT_FAILURE
, _("%s not enabled for wakeup events"), devname
);
525 /* relative or absolute alarm time, normalized to time_t */
526 if (get_basetimes(&ctl
, fd
) < 0)
530 printf(_("alarm %ld, sys_time %ld, rtc_time %ld, seconds %u\n"),
531 alarm
, ctl
.sys_time
, ctl
.rtc_time
, seconds
);
533 if (suspend
!= DISABLE_MODE
&& suspend
!= SHOW_MODE
) {
534 /* perform alarm setup when the show or disable modes are not set */
536 if (alarm
< ctl
.sys_time
)
537 errx(EXIT_FAILURE
, _("time doesn't go backward to %s"),
539 alarm
+= ctl
.sys_time
- ctl
.rtc_time
;
541 alarm
= ctl
.rtc_time
+ seconds
+ 1;
543 if (setup_alarm(&ctl
, fd
, &alarm
) < 0)
546 if (suspend
== NO_MODE
|| suspend
== ON_MODE
)
547 printf(_("%s: wakeup using %s at %s"),
548 program_invocation_short_name
, devname
,
551 printf(_("%s: wakeup from \"%s\" using %s at %s"),
552 program_invocation_short_name
, ctl
.mode_str
, devname
,
561 printf(_("suspend mode: no; leaving\n"));
562 ctl
.dryrun
= 1; /* to skip disabling alarm at the end */
570 printf(_("suspend mode: off; executing %s\n"),
572 arg
[i
++] = _PATH_SHUTDOWN
;
579 warn(_("failed to execute %s"), _PATH_SHUTDOWN
);
589 printf(_("suspend mode: on; reading rtc\n"));
592 t
= read(fd
, &data
, sizeof data
);
594 warn(_("rtc read failed"));
598 printf("... %s: %03lx\n", devname
, data
);
599 } while (!(data
& RTC_AF
));
604 /* just break, alarm gets disabled in the end */
606 printf(_("suspend mode: disable; disabling alarm\n"));
610 printf(_("suspend mode: show; printing alarm info\n"));
611 if (print_alarm(&ctl
, fd
))
613 ctl
.dryrun
= 1; /* don't really disable alarm in the end, just show */
617 printf(_("suspend mode: %s; suspending system\n"), ctl
.mode_str
);
619 suspend_system(&ctl
);
623 struct rtc_wkalrm wake
;
625 if (ioctl(fd
, RTC_WKALM_RD
, &wake
) < 0) {
626 warn(_("read rtc alarm failed"));
630 if (ioctl(fd
, RTC_WKALM_SET
, &wake
) < 0) {
631 warn(_("disable rtc alarm interrupt failed"));