2 * SPDX-License-Identifier: GPL-2.0-or-later
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * Copyright (C) 2007 Bernhard Walle <bwalle@suse.de>
10 * Copyright (C) 2007-2023 Karel Zak <kzak@redhat.com>
12 * rtcwake -- enter a system sleep state until specified wakeup time.
14 * This uses cross-platform Linux interfaces to enter a system sleep state,
15 * and leave it no later than a specified time. It uses any RTC framework
16 * driver that supports standard driver model wakeup flags.
18 * This is normally used like the old "apmsleep" utility, to wake from a
19 * suspend state like ACPI S1 (standby) or S3 (suspend-to-RAM). Most
20 * platforms can implement those without analogues of BIOS, APM, or ACPI.
22 * On some systems, this can also be used like "nvram-wakeup", waking
23 * from states like ACPI S4 (suspend to disk). Not all systems have
24 * persistent media that are appropriate for such suspend modes.
26 * The best way to set the system's RTC is so that it holds the current
27 * time in UTC. Use the "-l" flag to tell this program that the system
28 * RTC uses a local timezone instead (maybe you dual-boot MS-Windows).
29 * That flag should not be needed on systems with adjtime support.
34 #include <linux/rtc.h>
39 #include <sys/ioctl.h>
42 #include <sys/types.h>
48 #include "closestream.h"
52 #include "pathnames.h"
55 #include "timeutils.h"
59 # define RTC_AF 0x20 /* Alarm interrupt */
62 #define ADJTIME_ZONE_BUFSIZ 8
63 #define SYS_WAKEUP_PATH_TEMPLATE "/sys/class/rtc/%s/device/power/wakeup"
64 #define SYS_POWER_STATE_PATH "/sys/power/state"
65 #define DEFAULT_RTC_DEVICE "/dev/rtc0"
67 enum rtc_modes
{ /* manual page --mode option explains these. */
74 SYSFS_MODE
/* keep it last */
78 static const char *rtcwake_mode_string
[] = {
82 [DISABLE_MODE
] = "disable",
92 struct rtcwake_control
{
93 char *mode_str
; /* name of the requested mode */
94 char **possible_modes
; /* modes listed in /sys/power/state */
95 char *adjfile
; /* adjtime file path */
96 enum clock_modes clock_mode
; /* hwclock timezone */
97 time_t sys_time
; /* system time */
98 time_t rtc_time
; /* hardware time */
99 unsigned int verbose
:1, /* verbose messaging */
100 dryrun
:1; /* do not set alarm, suspend system, etc */
103 static void __attribute__((__noreturn__
)) usage(void)
106 fputs(USAGE_HEADER
, out
);
108 _(" %s [options]\n"), program_invocation_short_name
);
110 fputs(USAGE_SEPARATOR
, out
);
111 fputs(_("Enter a system sleep state until a specified wakeup time.\n"), out
);
113 fputs(USAGE_OPTIONS
, out
);
114 fputs(_(" -a, --auto reads the clock mode from adjust file (default)\n"), out
);
116 _(" -A, --adjfile <file> specifies the path to the adjust file\n"
117 " the default is %s\n"), _PATH_ADJTIME
);
118 fputs(_(" --date <timestamp> date time of timestamp to wake\n"), out
);
119 fputs(_(" -d, --device <device> select rtc device (rtc0|rtc1|...)\n"), out
);
120 fputs(_(" -n, --dry-run does everything, but suspend\n"), out
);
121 fputs(_(" -l, --local RTC uses local timezone\n"), out
);
122 fputs(_(" --list-modes list available modes\n"), out
);
123 fputs(_(" -m, --mode <mode> standby|mem|... sleep mode\n"), out
);
124 fputs(_(" -s, --seconds <seconds> seconds to sleep\n"), out
);
125 fputs(_(" -t, --time <time_t> time to wake\n"), out
);
126 fputs(_(" -u, --utc RTC uses UTC\n"), out
);
127 fputs(_(" -v, --verbose verbose messages\n"), out
);
129 fputs(USAGE_SEPARATOR
, out
);
130 fprintf(out
, USAGE_HELP_OPTIONS(26));
131 fprintf(out
, USAGE_MAN_TAIL("rtcwake(8)"));
135 static int is_wakeup_enabled(const char *devname
)
141 if (startswith(devname
, "/dev/"))
143 snprintf(buf
, sizeof buf
, SYS_WAKEUP_PATH_TEMPLATE
, devname
+ skip
);
146 warn(_("cannot open %s"), buf
);
150 s
= fgets(buf
, sizeof buf
, f
);
154 s
= strchr(buf
, '\n');
158 /* wakeup events could be disabled or not supported */
159 return strcmp(buf
, "enabled") == 0;
162 static int get_basetimes(struct rtcwake_control
*ctl
, int fd
)
164 struct tm tm
= { 0 };
167 /* This process works in RTC time, except when working
168 * with the system clock (which always uses UTC).
170 if (ctl
->clock_mode
== CM_UTC
)
171 xsetenv("TZ", "UTC", 1);
173 /* Read rtc and system clocks "at the same time", or as
174 * precisely (+/- a second) as we can read them.
176 if (ioctl(fd
, RTC_RD_TIME
, &rtc
) < 0) {
177 warn(_("read rtc time failed"));
181 ctl
->sys_time
= time(NULL
);
182 if (ctl
->sys_time
== (time_t)-1) {
183 warn(_("read system time failed"));
186 /* Convert rtc_time to normal arithmetic-friendly form,
187 * updating tm.tm_wday as used by asctime().
189 tm
.tm_sec
= rtc
.tm_sec
;
190 tm
.tm_min
= rtc
.tm_min
;
191 tm
.tm_hour
= rtc
.tm_hour
;
192 tm
.tm_mday
= rtc
.tm_mday
;
193 tm
.tm_mon
= rtc
.tm_mon
;
194 tm
.tm_year
= rtc
.tm_year
;
195 tm
.tm_isdst
= -1; /* assume the system knows better than the RTC */
197 ctl
->rtc_time
= mktime(&tm
);
198 if (ctl
->rtc_time
== (time_t)-1) {
199 warn(_("convert rtc time failed"));
204 /* Unless the system uses UTC, either delta or tzone
205 * reflects a seconds offset from UTC. The value can
206 * help sort out problems like bugs in your C library. */
208 printf("\tdelta = %"PRId64
"\n", (int64_t) ctl
->sys_time
- ctl
->rtc_time
);
209 printf("\ttzone = %ld\n", timezone
);
210 printf("\ttzname = %s\n", tzname
[daylight
]);
211 gmtime_r(&ctl
->sys_time
, &tm
);
212 printf("\tsystime = %"PRId64
", (UTC) %s",
213 (int64_t) ctl
->sys_time
, asctime_r(&tm
, s
));
214 gmtime_r(&ctl
->rtc_time
, &tm
);
215 printf("\trtctime = %"PRId64
", (UTC) %s",
216 (int64_t) ctl
->rtc_time
, asctime_r(&tm
, s
));
221 static int setup_alarm(struct rtcwake_control
*ctl
, int fd
, time_t *wakeup
)
224 struct rtc_wkalrm wake
= { 0 };
226 /* The wakeup time is in POSIX time (more or less UTC). Ideally
227 * RTCs use that same time; but PCs can't do that if they need to
228 * boot MS-Windows. Messy...
230 * When clock_mode == CM_UTC this process's timezone is UTC, so
231 * we'll pass a UTC date to the RTC.
233 * Else clock_mode == CM_LOCAL so the time given to the RTC will
234 * instead use the local time zone. */
235 localtime_r(wakeup
, &tm
);
236 wake
.time
.tm_sec
= tm
.tm_sec
;
237 wake
.time
.tm_min
= tm
.tm_min
;
238 wake
.time
.tm_hour
= tm
.tm_hour
;
239 wake
.time
.tm_mday
= tm
.tm_mday
;
240 wake
.time
.tm_mon
= tm
.tm_mon
;
241 wake
.time
.tm_year
= tm
.tm_year
;
242 /* wday, yday, and isdst fields are unused */
243 wake
.time
.tm_wday
= -1;
244 wake
.time
.tm_yday
= -1;
245 wake
.time
.tm_isdst
= -1;
248 if (!ctl
->dryrun
&& ioctl(fd
, RTC_WKALM_SET
, &wake
) < 0) {
249 warn(_("set rtc wake alarm failed"));
255 static char **get_sys_power_states(struct rtcwake_control
*ctl
)
259 if (!ctl
->possible_modes
) {
260 char buf
[256] = { 0 };
263 fd
= open(SYS_POWER_STATE_PATH
, O_RDONLY
);
266 ss
= read(fd
, &buf
, sizeof(buf
) - 1);
270 ctl
->possible_modes
= strv_split(buf
, " \n");
273 return ctl
->possible_modes
;
280 static void wait_stdin(struct rtcwake_control
*ctl
)
282 struct pollfd fd
[] = {
283 {.fd
= STDIN_FILENO
, .events
= POLLIN
}
287 while (tries
< 8 && poll(fd
, 1, 10) == 1) {
289 warnx(_("discarding stdin"));
291 tcflush(STDIN_FILENO
, TCIFLUSH
);
296 static void suspend_system(struct rtcwake_control
*ctl
)
298 FILE *f
= fopen(SYS_POWER_STATE_PATH
, "w");
301 warn(_("cannot open %s"), SYS_POWER_STATE_PATH
);
306 if (isatty(STDIN_FILENO
))
308 fprintf(f
, "%s\n", ctl
->mode_str
);
311 /* this executes after wake from suspend */
313 errx(EXIT_FAILURE
, _("write error"));
316 static int read_clock_mode(struct rtcwake_control
*ctl
)
319 char linebuf
[ADJTIME_ZONE_BUFSIZ
];
321 fp
= fopen(ctl
->adjfile
, "r");
325 if (skip_fline(fp
) || skip_fline(fp
)) {
329 /* read third line */
330 if (!fgets(linebuf
, sizeof linebuf
, fp
)) {
335 if (strncmp(linebuf
, "UTC", 3) == 0)
336 ctl
->clock_mode
= CM_UTC
;
337 else if (strncmp(linebuf
, "LOCAL", 5) == 0)
338 ctl
->clock_mode
= CM_LOCAL
;
339 else if (ctl
->verbose
)
340 warnx(_("unexpected third line in: %s: %s"), ctl
->adjfile
, linebuf
);
346 static int print_alarm(struct rtcwake_control
*ctl
, int fd
)
348 struct rtc_wkalrm wake
;
349 struct tm tm
= { 0 };
351 char s
[CTIME_BUFSIZ
];
353 if (ioctl(fd
, RTC_WKALM_RD
, &wake
) < 0) {
354 warn(_("read rtc alarm failed"));
358 if (wake
.enabled
!= 1 || wake
.time
.tm_year
== -1) {
359 printf(_("alarm: off\n"));
362 tm
.tm_sec
= wake
.time
.tm_sec
;
363 tm
.tm_min
= wake
.time
.tm_min
;
364 tm
.tm_hour
= wake
.time
.tm_hour
;
365 tm
.tm_mday
= wake
.time
.tm_mday
;
366 tm
.tm_mon
= wake
.time
.tm_mon
;
367 tm
.tm_year
= wake
.time
.tm_year
;
368 tm
.tm_isdst
= -1; /* assume the system knows better than the RTC */
371 if (alarm
== (time_t)-1) {
372 warn(_("convert time failed"));
375 /* 0 if both UTC, or expresses diff if RTC in local time */
376 alarm
+= ctl
->sys_time
- ctl
->rtc_time
;
378 printf(_("alarm: on %s"), s
);
383 static int get_rtc_mode(struct rtcwake_control
*ctl
, const char *s
)
386 char **modes
= get_sys_power_states(ctl
), **m
;
388 STRV_FOREACH(m
, modes
) {
389 if (strcmp(s
, *m
) == 0)
393 for (i
= 0; i
< ARRAY_SIZE(rtcwake_mode_string
); i
++)
394 if (!strcmp(s
, rtcwake_mode_string
[i
]))
400 static int open_dev_rtc(const char *devname
)
403 char *devpath
= NULL
;
405 if (startswith(devname
, "/dev"))
406 devpath
= xstrdup(devname
);
408 xasprintf(&devpath
, "/dev/%s", devname
);
409 fd
= open(devpath
, O_RDONLY
| O_CLOEXEC
);
411 err(EXIT_FAILURE
, _("%s: unable to find device"), devpath
);
416 static void list_modes(struct rtcwake_control
*ctl
)
419 char **modes
= get_sys_power_states(ctl
), **m
;
422 errx(EXIT_FAILURE
, _("could not read: %s"), SYS_POWER_STATE_PATH
);
424 STRV_FOREACH(m
, modes
)
427 for (i
= 0; i
< ARRAY_SIZE(rtcwake_mode_string
); i
++)
428 printf("%s ", rtcwake_mode_string
[i
]);
432 int main(int argc
, char **argv
)
434 struct rtcwake_control ctl
= {
435 .mode_str
= "suspend", /* default mode */
436 .adjfile
= _PATH_ADJTIME
,
437 .clock_mode
= CM_AUTO
439 char *devname
= DEFAULT_RTC_DEVICE
;
440 int suspend
= SYSFS_MODE
;
441 int rc
= EXIT_SUCCESS
;
444 time_t alarm
= 0, seconds
= 0;
446 OPT_DATE
= CHAR_MAX
+ 1,
449 static const struct option long_options
[] = {
450 { "adjfile", required_argument
, NULL
, 'A' },
451 { "auto", no_argument
, NULL
, 'a' },
452 { "dry-run", no_argument
, NULL
, 'n' },
453 { "local", no_argument
, NULL
, 'l' },
454 { "utc", no_argument
, NULL
, 'u' },
455 { "verbose", no_argument
, NULL
, 'v' },
456 { "version", no_argument
, NULL
, 'V' },
457 { "help", no_argument
, NULL
, 'h' },
458 { "mode", required_argument
, NULL
, 'm' },
459 { "device", required_argument
, NULL
, 'd' },
460 { "seconds", required_argument
, NULL
, 's' },
461 { "time", required_argument
, NULL
, 't' },
462 { "date", required_argument
, NULL
, OPT_DATE
},
463 { "list-modes", no_argument
, NULL
, OPT_LIST
},
466 static const ul_excl_t excl
[] = {
468 { 's', 't', OPT_DATE
},
471 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
473 setlocale(LC_ALL
, "");
474 bindtextdomain(PACKAGE
, LOCALEDIR
);
476 close_stdout_atexit();
478 while ((t
= getopt_long(argc
, argv
, "A:ahd:lm:ns:t:uVv",
479 long_options
, NULL
)) != EOF
) {
480 err_exclusive_options(t
, long_options
, excl
, excl_st
);
483 /* for better compatibility with hwclock */
484 ctl
.adjfile
= optarg
;
487 ctl
.clock_mode
= CM_AUTO
;
493 ctl
.clock_mode
= CM_LOCAL
;
501 if ((suspend
= get_rtc_mode(&ctl
, optarg
)) < 0)
502 errx(EXIT_FAILURE
, _("unrecognized suspend state '%s'"), optarg
);
503 ctl
.mode_str
= optarg
;
509 /* alarm time, seconds-to-sleep (relative) */
510 seconds
= strtotime_or_err(optarg
, _("invalid seconds argument"));
513 /* alarm time, time_t (absolute, seconds since epoch) */
514 alarm
= strtotime_or_err(optarg
, _("invalid time argument"));
517 { /* alarm time, see timestamp format from manual */
519 if (parse_timestamp(optarg
, &p
) < 0)
520 errx(EXIT_FAILURE
, _("invalid time value \"%s\""), optarg
);
521 alarm
= (time_t) (p
/ 1000000);
525 ctl
.clock_mode
= CM_UTC
;
532 print_version(EXIT_SUCCESS
);
536 errtryhelp(EXIT_FAILURE
);
540 if (ctl
.clock_mode
== CM_AUTO
&& read_clock_mode(&ctl
) < 0) {
541 printf(_("%s: assuming RTC uses UTC ...\n"), program_invocation_short_name
);
542 ctl
.clock_mode
= CM_UTC
;
546 printf("%s", ctl
.clock_mode
== CM_UTC
? _("Using UTC time.\n") :
547 _("Using local time.\n"));
549 if (!alarm
&& !seconds
&& suspend
!= DISABLE_MODE
&& suspend
!= SHOW_MODE
)
550 errx(EXIT_FAILURE
, _("must provide wake time (see --seconds, --time and --date options)"));
552 /* device must exist and (if we'll sleep) be wakeup-enabled */
553 fd
= open_dev_rtc(devname
);
555 if (suspend
!= ON_MODE
&& suspend
!= NO_MODE
&& !is_wakeup_enabled(devname
))
556 errx(EXIT_FAILURE
, _("%s not enabled for wakeup events"), devname
);
558 /* relative or absolute alarm time, normalized to time_t */
559 if (get_basetimes(&ctl
, fd
) < 0)
563 printf(_("alarm %"PRId64
", sys_time %"PRId64
", "
564 "rtc_time %"PRId64
", seconds %"PRIu64
"\n"),
565 (int64_t) alarm
, (int64_t) ctl
.sys_time
,
566 (int64_t) ctl
.rtc_time
,
569 if (suspend
!= DISABLE_MODE
&& suspend
!= SHOW_MODE
) {
570 /* perform alarm setup when the show or disable modes are not set */
572 if (alarm
< ctl
.sys_time
) {
573 char s
[CTIME_BUFSIZ
];
576 errx(EXIT_FAILURE
, _("time doesn't go backward to %s"), s
);
578 alarm
-= ctl
.sys_time
- ctl
.rtc_time
;
580 alarm
= ctl
.rtc_time
+ seconds
+ 1;
582 if (setup_alarm(&ctl
, fd
, &alarm
) < 0)
585 if (suspend
== NO_MODE
|| suspend
== ON_MODE
) {
586 char s
[CTIME_BUFSIZ
];
589 printf(_("%s: wakeup using %s at %s"),
590 program_invocation_short_name
, devname
, s
);
592 char s
[CTIME_BUFSIZ
];
595 printf(_("%s: wakeup from \"%s\" using %s at %s"),
596 program_invocation_short_name
, ctl
.mode_str
, devname
, s
);
605 printf(_("suspend mode: no; leaving\n"));
606 ctl
.dryrun
= 1; /* to skip disabling alarm at the end */
613 if (!access(_PATH_SHUTDOWN
, X_OK
)) {
614 arg
[i
++] = _PATH_SHUTDOWN
;
619 } else if (!access(_PATH_POWEROFF
, X_OK
)) {
620 arg
[i
++] = _PATH_POWEROFF
;
628 printf(_("suspend mode: off; executing %s\n"),
632 warn(_("failed to execute %s"), arg
[0]);
636 /* Failed to find shutdown command */
637 warn(_("failed to find shutdown command"));
647 printf(_("suspend mode: on; reading rtc\n"));
650 t
= read(fd
, &data
, sizeof data
);
652 warn(_("rtc read failed"));
656 printf("... %s: %03lx\n", devname
, data
);
657 } while (!(data
& RTC_AF
));
662 /* just break, alarm gets disabled in the end */
664 printf(_("suspend mode: disable; disabling alarm\n"));
668 printf(_("suspend mode: show; printing alarm info\n"));
669 if (print_alarm(&ctl
, fd
))
671 ctl
.dryrun
= 1; /* don't really disable alarm in the end, just show */
675 printf(_("suspend mode: %s; suspending system\n"), ctl
.mode_str
);
677 suspend_system(&ctl
);
681 struct rtc_wkalrm wake
;
683 if (ioctl(fd
, RTC_WKALM_RD
, &wake
) < 0) {
684 warn(_("read rtc alarm failed"));
688 if (ioctl(fd
, RTC_WKALM_SET
, &wake
) < 0) {
689 warn(_("disable rtc alarm interrupt failed"));