]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/rtcwake.c
libmount: don't use sscanf() for swaps parsing
[thirdparty/util-linux.git] / sys-utils / rtcwake.c
1 /*
2 * rtcwake -- enter a system sleep state until specified wakeup time.
3 *
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.
7 *
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.
11 *
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.
15 *
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.
20 */
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <getopt.h>
25 #include <linux/rtc.h>
26 #include <poll.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/ioctl.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <termios.h>
34 #include <time.h>
35 #include <unistd.h>
36
37 #include "c.h"
38 #include "closestream.h"
39 #include "env.h"
40 #include "nls.h"
41 #include "optutils.h"
42 #include "pathnames.h"
43 #include "strutils.h"
44 #include "strv.h"
45 #include "timeutils.h"
46 #include "xalloc.h"
47
48 #ifndef RTC_AF
49 # define RTC_AF 0x20 /* Alarm interrupt */
50 #endif
51
52 #define ADJTIME_ZONE_BUFSIZ 8
53 #define SYS_WAKEUP_PATH_TEMPLATE "/sys/class/rtc/%s/device/power/wakeup"
54 #define SYS_POWER_STATE_PATH "/sys/power/state"
55 #define DEFAULT_RTC_DEVICE "/dev/rtc0"
56
57 enum rtc_modes { /* manual page --mode option explains these. */
58 OFF_MODE = 0,
59 NO_MODE,
60 ON_MODE,
61 DISABLE_MODE,
62 SHOW_MODE,
63
64 SYSFS_MODE /* keep it last */
65
66 };
67
68 static const char *rtcwake_mode_string[] = {
69 [OFF_MODE] = "off",
70 [NO_MODE] = "no",
71 [ON_MODE] = "on",
72 [DISABLE_MODE] = "disable",
73 [SHOW_MODE] = "show"
74 };
75
76 enum clock_modes {
77 CM_AUTO,
78 CM_UTC,
79 CM_LOCAL
80 };
81
82 struct rtcwake_control {
83 char *mode_str; /* name of the requested mode */
84 char **possible_modes; /* modes listed in /sys/power/state */
85 char *adjfile; /* adjtime file path */
86 enum clock_modes clock_mode; /* hwclock timezone */
87 time_t sys_time; /* system time */
88 time_t rtc_time; /* hardware time */
89 unsigned int verbose:1, /* verbose messaging */
90 dryrun:1; /* do not set alarm, suspend system, etc */
91 };
92
93 static void __attribute__((__noreturn__)) usage(void)
94 {
95 FILE *out = stdout;
96 fputs(USAGE_HEADER, out);
97 fprintf(out,
98 _(" %s [options]\n"), program_invocation_short_name);
99
100 fputs(USAGE_SEPARATOR, out);
101 fputs(_("Enter a system sleep state until a specified wakeup time.\n"), out);
102
103 fputs(USAGE_OPTIONS, out);
104 fputs(_(" -a, --auto reads the clock mode from adjust file (default)\n"), out);
105 fprintf(out,
106 _(" -A, --adjfile <file> specifies the path to the adjust file\n"
107 " the default is %s\n"), _PATH_ADJTIME);
108 fputs(_(" --date <timestamp> date time of timestamp to wake\n"), out);
109 fputs(_(" -d, --device <device> select rtc device (rtc0|rtc1|...)\n"), out);
110 fputs(_(" -n, --dry-run does everything, but suspend\n"), out);
111 fputs(_(" -l, --local RTC uses local timezone\n"), out);
112 fputs(_(" --list-modes list available modes\n"), out);
113 fputs(_(" -m, --mode <mode> standby|mem|... sleep mode\n"), out);
114 fputs(_(" -s, --seconds <seconds> seconds to sleep\n"), out);
115 fputs(_(" -t, --time <time_t> time to wake\n"), out);
116 fputs(_(" -u, --utc RTC uses UTC\n"), out);
117 fputs(_(" -v, --verbose verbose messages\n"), out);
118
119 fputs(USAGE_SEPARATOR, out);
120 printf(USAGE_HELP_OPTIONS(26));
121 printf(USAGE_MAN_TAIL("rtcwake(8)"));
122 exit(EXIT_SUCCESS);
123 }
124
125 static int is_wakeup_enabled(const char *devname)
126 {
127 char buf[128], *s;
128 FILE *f;
129 size_t skip = 0;
130
131 if (startswith(devname, "/dev/"))
132 skip = 5;
133 snprintf(buf, sizeof buf, SYS_WAKEUP_PATH_TEMPLATE, devname + skip);
134 f = fopen(buf, "r");
135 if (!f) {
136 warn(_("cannot open %s"), buf);
137 return 0;
138 }
139
140 s = fgets(buf, sizeof buf, f);
141 fclose(f);
142 if (!s)
143 return 0;
144 s = strchr(buf, '\n');
145 if (!s)
146 return 0;
147 *s = 0;
148 /* wakeup events could be disabled or not supported */
149 return strcmp(buf, "enabled") == 0;
150 }
151
152 static int get_basetimes(struct rtcwake_control *ctl, int fd)
153 {
154 struct tm tm = { 0 };
155 struct rtc_time rtc;
156
157 /* This process works in RTC time, except when working
158 * with the system clock (which always uses UTC).
159 */
160 if (ctl->clock_mode == CM_UTC)
161 xsetenv("TZ", "UTC", 1);
162 tzset();
163 /* Read rtc and system clocks "at the same time", or as
164 * precisely (+/- a second) as we can read them.
165 */
166 if (ioctl(fd, RTC_RD_TIME, &rtc) < 0) {
167 warn(_("read rtc time failed"));
168 return -1;
169 }
170
171 ctl->sys_time = time(NULL);
172 if (ctl->sys_time == (time_t)-1) {
173 warn(_("read system time failed"));
174 return -1;
175 }
176 /* Convert rtc_time to normal arithmetic-friendly form,
177 * updating tm.tm_wday as used by asctime().
178 */
179 tm.tm_sec = rtc.tm_sec;
180 tm.tm_min = rtc.tm_min;
181 tm.tm_hour = rtc.tm_hour;
182 tm.tm_mday = rtc.tm_mday;
183 tm.tm_mon = rtc.tm_mon;
184 tm.tm_year = rtc.tm_year;
185 tm.tm_isdst = -1; /* assume the system knows better than the RTC */
186
187 ctl->rtc_time = mktime(&tm);
188 if (ctl->rtc_time == (time_t)-1) {
189 warn(_("convert rtc time failed"));
190 return -1;
191 }
192
193 if (ctl->verbose) {
194 /* Unless the system uses UTC, either delta or tzone
195 * reflects a seconds offset from UTC. The value can
196 * help sort out problems like bugs in your C library. */
197 printf("\tdelta = %ld\n", ctl->sys_time - ctl->rtc_time);
198 printf("\ttzone = %ld\n", timezone);
199 printf("\ttzname = %s\n", tzname[daylight]);
200 gmtime_r(&ctl->rtc_time, &tm);
201 printf("\tsystime = %ld, (UTC) %s",
202 (long) ctl->sys_time, asctime(gmtime(&ctl->sys_time)));
203 printf("\trtctime = %ld, (UTC) %s",
204 (long) ctl->rtc_time, asctime(&tm));
205 }
206 return 0;
207 }
208
209 static int setup_alarm(struct rtcwake_control *ctl, int fd, time_t *wakeup)
210 {
211 struct tm *tm;
212 struct rtc_wkalrm wake = { 0 };
213
214 /* The wakeup time is in POSIX time (more or less UTC). Ideally
215 * RTCs use that same time; but PCs can't do that if they need to
216 * boot MS-Windows. Messy...
217 *
218 * When clock_mode == CM_UTC this process's timezone is UTC, so
219 * we'll pass a UTC date to the RTC.
220 *
221 * Else clock_mode == CM_LOCAL so the time given to the RTC will
222 * instead use the local time zone. */
223 tm = localtime(wakeup);
224 wake.time.tm_sec = tm->tm_sec;
225 wake.time.tm_min = tm->tm_min;
226 wake.time.tm_hour = tm->tm_hour;
227 wake.time.tm_mday = tm->tm_mday;
228 wake.time.tm_mon = tm->tm_mon;
229 wake.time.tm_year = tm->tm_year;
230 /* wday, yday, and isdst fields are unused */
231 wake.time.tm_wday = -1;
232 wake.time.tm_yday = -1;
233 wake.time.tm_isdst = -1;
234 wake.enabled = 1;
235
236 if (!ctl->dryrun && ioctl(fd, RTC_WKALM_SET, &wake) < 0) {
237 warn(_("set rtc wake alarm failed"));
238 return -1;
239 }
240 return 0;
241 }
242
243 static char **get_sys_power_states(struct rtcwake_control *ctl)
244 {
245 int fd = -1;
246
247 if (!ctl->possible_modes) {
248 char buf[256] = { 0 };
249
250 fd = open(SYS_POWER_STATE_PATH, O_RDONLY);
251 if (fd < 0)
252 goto nothing;
253 if (read(fd, &buf, sizeof(buf) - 1) <= 0)
254 goto nothing;
255 ctl->possible_modes = strv_split(buf, " \n");
256 close(fd);
257 }
258 return ctl->possible_modes;
259 nothing:
260 if (fd >= 0)
261 close(fd);
262 return NULL;
263 }
264
265 static void wait_stdin(struct rtcwake_control *ctl)
266 {
267 struct pollfd fd[] = {
268 {.fd = STDIN_FILENO, .events = POLLIN}
269 };
270 int tries = 0;
271
272 while (tries < 8 && poll(fd, 1, 10) == 1) {
273 if (ctl->verbose)
274 warnx(_("discarding stdin"));
275 xusleep(250000);
276 tcflush(STDIN_FILENO, TCIFLUSH);
277 tries++;
278 }
279 }
280
281 static void suspend_system(struct rtcwake_control *ctl)
282 {
283 FILE *f = fopen(SYS_POWER_STATE_PATH, "w");
284
285 if (!f) {
286 warn(_("cannot open %s"), SYS_POWER_STATE_PATH);
287 return;
288 }
289
290 if (!ctl->dryrun) {
291 if (isatty(STDIN_FILENO))
292 wait_stdin(ctl);
293 fprintf(f, "%s\n", ctl->mode_str);
294 fflush(f);
295 }
296 /* this executes after wake from suspend */
297 if (close_stream(f))
298 errx(EXIT_FAILURE, _("write error"));
299 }
300
301 static int read_clock_mode(struct rtcwake_control *ctl)
302 {
303 FILE *fp;
304 char linebuf[ADJTIME_ZONE_BUFSIZ];
305
306 fp = fopen(ctl->adjfile, "r");
307 if (!fp)
308 return -1;
309 /* skip two lines */
310 if (skip_fline(fp) || skip_fline(fp)) {
311 fclose(fp);
312 return -1;
313 }
314 /* read third line */
315 if (!fgets(linebuf, sizeof linebuf, fp)) {
316 fclose(fp);
317 return -1;
318 }
319
320 if (strncmp(linebuf, "UTC", 3) == 0)
321 ctl->clock_mode = CM_UTC;
322 else if (strncmp(linebuf, "LOCAL", 5) == 0)
323 ctl->clock_mode = CM_LOCAL;
324 else if (ctl->verbose)
325 warnx(_("unexpected third line in: %s: %s"), ctl->adjfile, linebuf);
326
327 fclose(fp);
328 return 0;
329 }
330
331 static int print_alarm(struct rtcwake_control *ctl, int fd)
332 {
333 struct rtc_wkalrm wake;
334 struct tm tm = { 0 };
335 time_t alarm;
336
337 if (ioctl(fd, RTC_WKALM_RD, &wake) < 0) {
338 warn(_("read rtc alarm failed"));
339 return -1;
340 }
341
342 if (wake.enabled != 1 || wake.time.tm_year == -1) {
343 printf(_("alarm: off\n"));
344 return 0;
345 }
346 tm.tm_sec = wake.time.tm_sec;
347 tm.tm_min = wake.time.tm_min;
348 tm.tm_hour = wake.time.tm_hour;
349 tm.tm_mday = wake.time.tm_mday;
350 tm.tm_mon = wake.time.tm_mon;
351 tm.tm_year = wake.time.tm_year;
352 tm.tm_isdst = -1; /* assume the system knows better than the RTC */
353
354 alarm = mktime(&tm);
355 if (alarm == (time_t)-1) {
356 warn(_("convert time failed"));
357 return -1;
358 }
359 /* 0 if both UTC, or expresses diff if RTC in local time */
360 alarm += ctl->sys_time - ctl->rtc_time;
361 printf(_("alarm: on %s"), ctime(&alarm));
362
363 return 0;
364 }
365
366 static int get_rtc_mode(struct rtcwake_control *ctl, const char *s)
367 {
368 size_t i;
369 char **modes = get_sys_power_states(ctl), **m;
370
371 STRV_FOREACH(m, modes) {
372 if (strcmp(s, *m) == 0)
373 return SYSFS_MODE;
374 }
375
376 for (i = 0; i < ARRAY_SIZE(rtcwake_mode_string); i++)
377 if (!strcmp(s, rtcwake_mode_string[i]))
378 return i;
379
380 return -EINVAL;
381 }
382
383 static int open_dev_rtc(const char *devname)
384 {
385 int fd;
386 char *devpath = NULL;
387
388 if (startswith(devname, "/dev"))
389 devpath = xstrdup(devname);
390 else
391 xasprintf(&devpath, "/dev/%s", devname);
392 fd = open(devpath, O_RDONLY | O_CLOEXEC);
393 if (fd < 0)
394 err(EXIT_FAILURE, _("%s: unable to find device"), devpath);
395 free(devpath);
396 return fd;
397 }
398
399 static void list_modes(struct rtcwake_control *ctl)
400 {
401 size_t i;
402 char **modes = get_sys_power_states(ctl), **m;
403
404 if (!modes)
405 errx(EXIT_FAILURE, _("could not read: %s"), SYS_POWER_STATE_PATH);
406
407 STRV_FOREACH(m, modes)
408 printf("%s ", *m);
409
410 for (i = 0; i < ARRAY_SIZE(rtcwake_mode_string); i++)
411 printf("%s ", rtcwake_mode_string[i]);
412 putchar('\n');
413 }
414
415 int main(int argc, char **argv)
416 {
417 struct rtcwake_control ctl = {
418 .mode_str = "suspend", /* default mode */
419 .adjfile = _PATH_ADJTIME,
420 .clock_mode = CM_AUTO
421 };
422 char *devname = DEFAULT_RTC_DEVICE;
423 unsigned seconds = 0;
424 int suspend = SYSFS_MODE;
425 int rc = EXIT_SUCCESS;
426 int t;
427 int fd;
428 time_t alarm = 0;
429 enum {
430 OPT_DATE = CHAR_MAX + 1,
431 OPT_LIST
432 };
433 static const struct option long_options[] = {
434 { "adjfile", required_argument, NULL, 'A' },
435 { "auto", no_argument, NULL, 'a' },
436 { "dry-run", no_argument, NULL, 'n' },
437 { "local", no_argument, NULL, 'l' },
438 { "utc", no_argument, NULL, 'u' },
439 { "verbose", no_argument, NULL, 'v' },
440 { "version", no_argument, NULL, 'V' },
441 { "help", no_argument, NULL, 'h' },
442 { "mode", required_argument, NULL, 'm' },
443 { "device", required_argument, NULL, 'd' },
444 { "seconds", required_argument, NULL, 's' },
445 { "time", required_argument, NULL, 't' },
446 { "date", required_argument, NULL, OPT_DATE },
447 { "list-modes", no_argument, NULL, OPT_LIST },
448 { NULL, 0, NULL, 0 }
449 };
450 static const ul_excl_t excl[] = {
451 { 'a', 'l', 'u' },
452 { 's', 't', OPT_DATE },
453 };
454 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
455
456 setlocale(LC_ALL, "");
457 bindtextdomain(PACKAGE, LOCALEDIR);
458 textdomain(PACKAGE);
459 atexit(close_stdout);
460
461 while ((t = getopt_long(argc, argv, "A:ahd:lm:ns:t:uVv",
462 long_options, NULL)) != EOF) {
463 err_exclusive_options(t, long_options, excl, excl_st);
464 switch (t) {
465 case 'A':
466 /* for better compatibility with hwclock */
467 ctl.adjfile = optarg;
468 break;
469 case 'a':
470 ctl.clock_mode = CM_AUTO;
471 break;
472 case 'd':
473 devname = optarg;
474 break;
475 case 'l':
476 ctl.clock_mode = CM_LOCAL;
477 break;
478
479 case OPT_LIST:
480 list_modes(&ctl);
481 return EXIT_SUCCESS;
482
483 case 'm':
484 if ((suspend = get_rtc_mode(&ctl, optarg)) < 0)
485 errx(EXIT_FAILURE, _("unrecognized suspend state '%s'"), optarg);
486 ctl.mode_str = optarg;
487 break;
488 case 'n':
489 ctl.dryrun = 1;
490 break;
491 case 's':
492 /* alarm time, seconds-to-sleep (relative) */
493 seconds = strtou32_or_err(optarg, _("invalid seconds argument"));
494 break;
495 case 't':
496 /* alarm time, time_t (absolute, seconds since epoch) */
497 alarm = strtou32_or_err(optarg, _("invalid time argument"));
498 break;
499 case OPT_DATE:
500 { /* alarm time, see timestamp format from manual */
501 usec_t p;
502 if (parse_timestamp(optarg, &p) < 0)
503 errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
504 alarm = (time_t) (p / 1000000);
505 break;
506 }
507 case 'u':
508 ctl.clock_mode = CM_UTC;
509 break;
510 case 'v':
511 ctl.verbose = 1;
512 break;
513 case 'V':
514 printf(UTIL_LINUX_VERSION);
515 exit(EXIT_SUCCESS);
516 case 'h':
517 usage();
518 default:
519 errtryhelp(EXIT_FAILURE);
520 }
521 }
522
523 if (ctl.clock_mode == CM_AUTO && read_clock_mode(&ctl) < 0) {
524 printf(_("%s: assuming RTC uses UTC ...\n"), program_invocation_short_name);
525 ctl.clock_mode = CM_UTC;
526 }
527
528 if (ctl.verbose)
529 printf("%s", ctl.clock_mode == CM_UTC ? _("Using UTC time.\n") :
530 _("Using local time.\n"));
531
532 if (!alarm && !seconds && suspend != DISABLE_MODE && suspend != SHOW_MODE)
533 errx(EXIT_FAILURE, _("must provide wake time (see --seconds, --time and --date options)"));
534
535 /* device must exist and (if we'll sleep) be wakeup-enabled */
536 fd = open_dev_rtc(devname);
537
538 if (suspend != ON_MODE && suspend != NO_MODE && !is_wakeup_enabled(devname))
539 errx(EXIT_FAILURE, _("%s not enabled for wakeup events"), devname);
540
541 /* relative or absolute alarm time, normalized to time_t */
542 if (get_basetimes(&ctl, fd) < 0)
543 exit(EXIT_FAILURE);
544
545 if (ctl.verbose)
546 printf(_("alarm %ld, sys_time %ld, rtc_time %ld, seconds %u\n"),
547 alarm, ctl.sys_time, ctl.rtc_time, seconds);
548
549 if (suspend != DISABLE_MODE && suspend != SHOW_MODE) {
550 /* perform alarm setup when the show or disable modes are not set */
551 if (alarm) {
552 if (alarm < ctl.sys_time)
553 errx(EXIT_FAILURE, _("time doesn't go backward to %s"),
554 ctime(&alarm));
555 alarm -= ctl.sys_time - ctl.rtc_time;
556 } else
557 alarm = ctl.rtc_time + seconds + 1;
558
559 if (setup_alarm(&ctl, fd, &alarm) < 0)
560 exit(EXIT_FAILURE);
561
562 if (suspend == NO_MODE || suspend == ON_MODE)
563 printf(_("%s: wakeup using %s at %s"),
564 program_invocation_short_name, devname,
565 ctime(&alarm));
566 else
567 printf(_("%s: wakeup from \"%s\" using %s at %s"),
568 program_invocation_short_name, ctl.mode_str, devname,
569 ctime(&alarm));
570 fflush(stdout);
571 xusleep(10 * 1000);
572 }
573
574 switch (suspend) {
575 case NO_MODE:
576 if (ctl.verbose)
577 printf(_("suspend mode: no; leaving\n"));
578 ctl.dryrun = 1; /* to skip disabling alarm at the end */
579 break;
580 case OFF_MODE:
581 {
582 char *arg[5];
583 int i = 0;
584
585 if (ctl.verbose)
586 printf(_("suspend mode: off; executing %s\n"),
587 _PATH_SHUTDOWN);
588 arg[i++] = _PATH_SHUTDOWN;
589 arg[i++] = "-h";
590 arg[i++] = "-P";
591 arg[i++] = "now";
592 arg[i] = NULL;
593 if (!ctl.dryrun) {
594 execv(arg[0], arg);
595 warn(_("failed to execute %s"), _PATH_SHUTDOWN);
596 rc = EXIT_FAILURE;
597 }
598 break;
599 }
600 case ON_MODE:
601 {
602 unsigned long data;
603
604 if (ctl.verbose)
605 printf(_("suspend mode: on; reading rtc\n"));
606 if (!ctl.dryrun) {
607 do {
608 t = read(fd, &data, sizeof data);
609 if (t < 0) {
610 warn(_("rtc read failed"));
611 break;
612 }
613 if (ctl.verbose)
614 printf("... %s: %03lx\n", devname, data);
615 } while (!(data & RTC_AF));
616 }
617 break;
618 }
619 case DISABLE_MODE:
620 /* just break, alarm gets disabled in the end */
621 if (ctl.verbose)
622 printf(_("suspend mode: disable; disabling alarm\n"));
623 break;
624 case SHOW_MODE:
625 if (ctl.verbose)
626 printf(_("suspend mode: show; printing alarm info\n"));
627 if (print_alarm(&ctl, fd))
628 rc = EXIT_FAILURE;
629 ctl.dryrun = 1; /* don't really disable alarm in the end, just show */
630 break;
631 default:
632 if (ctl.verbose)
633 printf(_("suspend mode: %s; suspending system\n"), ctl.mode_str);
634 sync();
635 suspend_system(&ctl);
636 }
637
638 if (!ctl.dryrun) {
639 struct rtc_wkalrm wake;
640
641 if (ioctl(fd, RTC_WKALM_RD, &wake) < 0) {
642 warn(_("read rtc alarm failed"));
643 rc = EXIT_FAILURE;
644 } else {
645 wake.enabled = 0;
646 if (ioctl(fd, RTC_WKALM_SET, &wake) < 0) {
647 warn(_("disable rtc alarm interrupt failed"));
648 rc = EXIT_FAILURE;
649 }
650 }
651 }
652
653 close(fd);
654 return rc;
655 }