]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/rtcwake.c
7c748dcc5ca927c7752c407da1b8f9004dfe3180
[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 <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/ioctl.h>
30 #include <sys/time.h>
31 #include <sys/types.h>
32 #include <time.h>
33 #include <unistd.h>
34
35 #include "c.h"
36 #include "closestream.h"
37 #include "nls.h"
38 #include "optutils.h"
39 #include "pathnames.h"
40 #include "strutils.h"
41 #include "strv.h"
42 #include "timeutils.h"
43 #include "xalloc.h"
44
45 #ifndef RTC_AF
46 # define RTC_AF 0x20 /* Alarm interrupt */
47 #endif
48
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"
53
54 enum rtc_modes { /* manual page --mode option explains these. */
55 OFF_MODE = 0,
56 NO_MODE,
57 ON_MODE,
58 DISABLE_MODE,
59 SHOW_MODE,
60
61 SYSFS_MODE /* keep it last */
62
63 };
64
65 static const char *rtcwake_mode_string[] = {
66 [OFF_MODE] = "off",
67 [NO_MODE] = "no",
68 [ON_MODE] = "on",
69 [DISABLE_MODE] = "disable",
70 [SHOW_MODE] = "show"
71 };
72
73 enum clock_modes {
74 CM_AUTO,
75 CM_UTC,
76 CM_LOCAL
77 };
78
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 */
88 };
89
90 static void __attribute__((__noreturn__)) usage(FILE *out)
91 {
92 fputs(USAGE_HEADER, out);
93 fprintf(out,
94 _(" %s [options]\n"), program_invocation_short_name);
95
96 fputs(USAGE_SEPARATOR, out);
97 fputs(_("Enter a system sleep state until a specified wakeup time.\n"), out);
98
99 fputs(USAGE_OPTIONS, out);
100 fputs(_(" -a, --auto reads the clock mode from adjust file (default)\n"), out);
101 fprintf(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);
114
115 printf(USAGE_SEPARATOR);
116 printf(USAGE_HELP);
117 printf(USAGE_VERSION);
118
119 printf(USAGE_MAN_TAIL("rtcwake(8)"));
120
121 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
122 }
123
124 static int is_wakeup_enabled(const char *devname)
125 {
126 char buf[128], *s;
127 FILE *f;
128 size_t skip = 0;
129
130 if (startswith(devname, "/dev/"))
131 skip = 5;
132 snprintf(buf, sizeof buf, SYS_WAKEUP_PATH_TEMPLATE, devname + skip);
133 f = fopen(buf, "r");
134 if (!f) {
135 warn(_("cannot open %s"), buf);
136 return 0;
137 }
138
139 s = fgets(buf, sizeof buf, f);
140 fclose(f);
141 if (!s)
142 return 0;
143 s = strchr(buf, '\n');
144 if (!s)
145 return 0;
146 *s = 0;
147 /* wakeup events could be disabled or not supported */
148 return strcmp(buf, "enabled") == 0;
149 }
150
151 static int get_basetimes(struct rtcwake_control *ctl, int fd)
152 {
153 struct tm tm = { 0 };
154 struct rtc_time rtc;
155
156 /* This process works in RTC time, except when working
157 * with the system clock (which always uses UTC).
158 */
159 if (ctl->clock_mode == CM_UTC)
160 setenv("TZ", "UTC", 1);
161 tzset();
162 /* Read rtc and system clocks "at the same time", or as
163 * precisely (+/- a second) as we can read them.
164 */
165 if (ioctl(fd, RTC_RD_TIME, &rtc) < 0) {
166 warn(_("read rtc time failed"));
167 return -1;
168 }
169
170 ctl->sys_time = time(0);
171 if (ctl->sys_time == (time_t)-1) {
172 warn(_("read system time failed"));
173 return -1;
174 }
175 /* Convert rtc_time to normal arithmetic-friendly form,
176 * updating tm.tm_wday as used by asctime().
177 */
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 */
185
186 ctl->rtc_time = mktime(&tm);
187 if (ctl->rtc_time == (time_t)-1) {
188 warn(_("convert rtc time failed"));
189 return -1;
190 }
191
192 if (ctl->verbose) {
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));
204 }
205 return 0;
206 }
207
208 static int setup_alarm(struct rtcwake_control *ctl, int fd, time_t *wakeup)
209 {
210 struct tm *tm;
211 struct rtc_wkalrm wake = { 0 };
212
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...
216 *
217 * When clock_mode == CM_UTC this process's timezone is UTC, so
218 * we'll pass a UTC date to the RTC.
219 *
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;
233 wake.enabled = 1;
234
235 if (!ctl->dryrun && ioctl(fd, RTC_WKALM_SET, &wake) < 0) {
236 warn(_("set rtc wake alarm failed"));
237 return -1;
238 }
239 return 0;
240 }
241
242 static char **get_sys_power_states(struct rtcwake_control *ctl)
243 {
244 int fd = -1;
245
246 if (!ctl->possible_modes) {
247 char buf[256] = { 0 };
248
249 fd = open(SYS_POWER_STATE_PATH, O_RDONLY);
250 if (fd < 0)
251 goto nothing;
252 if (read(fd, &buf, sizeof buf) <= 0)
253 goto nothing;
254 ctl->possible_modes = strv_split(buf, " \n");
255 close(fd);
256 }
257 return ctl->possible_modes;
258 nothing:
259 if (fd >= 0)
260 close(fd);
261 return NULL;
262 }
263
264 static void suspend_system(struct rtcwake_control *ctl)
265 {
266 FILE *f = fopen(SYS_POWER_STATE_PATH, "w");
267
268 if (!f) {
269 warn(_("cannot open %s"), SYS_POWER_STATE_PATH);
270 return;
271 }
272
273 if (!ctl->dryrun) {
274 fprintf(f, "%s\n", ctl->mode_str);
275 fflush(f);
276 }
277 /* this executes after wake from suspend */
278 if (close_stream(f))
279 errx(EXIT_FAILURE, _("write error"));
280 }
281
282 static int read_clock_mode(struct rtcwake_control *ctl)
283 {
284 FILE *fp;
285 char linebuf[ADJTIME_ZONE_BUFSIZ];
286
287 fp = fopen(ctl->adjfile, "r");
288 if (!fp)
289 return -1;
290 /* skip two lines */
291 if (skip_fline(fp) || skip_fline(fp)) {
292 fclose(fp);
293 return -1;
294 }
295 /* read third line */
296 if (!fgets(linebuf, sizeof linebuf, fp)) {
297 fclose(fp);
298 return -1;
299 }
300
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);
307
308 fclose(fp);
309 return 0;
310 }
311
312 static int print_alarm(struct rtcwake_control *ctl, int fd)
313 {
314 struct rtc_wkalrm wake;
315 struct tm tm = { 0 };
316 time_t alarm;
317
318 if (ioctl(fd, RTC_WKALM_RD, &wake) < 0) {
319 warn(_("read rtc alarm failed"));
320 return -1;
321 }
322
323 if (wake.enabled != 1 || wake.time.tm_year == -1) {
324 printf(_("alarm: off\n"));
325 return 0;
326 }
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 */
334
335 alarm = mktime(&tm);
336 if (alarm == (time_t)-1) {
337 warn(_("convert time failed"));
338 return -1;
339 }
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));
343
344 return 0;
345 }
346
347 static int get_rtc_mode(struct rtcwake_control *ctl, const char *optarg)
348 {
349 size_t i;
350 char **modes = get_sys_power_states(ctl), **m;
351
352 STRV_FOREACH(m, modes) {
353 if (strcmp(optarg, *m) == 0)
354 return SYSFS_MODE;
355 }
356
357 for (i = 0; i < ARRAY_SIZE(rtcwake_mode_string); i++)
358 if (!strcmp(optarg, rtcwake_mode_string[i]))
359 return i;
360
361 return -EINVAL;
362 }
363
364 static int open_dev_rtc(const char *devname)
365 {
366 int fd;
367 char *devpath = NULL;
368
369 if (startswith(devname, "/dev"))
370 devpath = xstrdup(devname);
371 else
372 xasprintf(&devpath, "/dev/%s", devname);
373 fd = open(devpath, O_RDONLY | O_CLOEXEC);
374 if (fd < 0)
375 err(EXIT_FAILURE, _("%s: unable to find device"), devpath);
376 free(devpath);
377 return fd;
378 }
379
380 static void list_modes(struct rtcwake_control *ctl)
381 {
382 size_t i;
383 char **modes = get_sys_power_states(ctl), **m;
384
385 if (!modes)
386 errx(EXIT_FAILURE, _("could not read: %s"), SYS_POWER_STATE_PATH);
387
388 STRV_FOREACH(m, modes)
389 printf("%s ", *m);
390
391 for (i = 0; i < ARRAY_SIZE(rtcwake_mode_string); i++)
392 printf("%s ", rtcwake_mode_string[i]);
393 putchar('\n');
394 }
395
396 int main(int argc, char **argv)
397 {
398 struct rtcwake_control ctl = {
399 .mode_str = "suspend", /* default mode */
400 .adjfile = _PATH_ADJTIME,
401 .clock_mode = CM_AUTO
402 };
403 char *devname = DEFAULT_RTC_DEVICE;
404 unsigned seconds = 0;
405 int suspend = SYSFS_MODE;
406 int rc = EXIT_SUCCESS;
407 int t;
408 int fd;
409 time_t alarm = 0;
410 enum {
411 OPT_DATE = CHAR_MAX + 1,
412 OPT_LIST
413 };
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},
429 {0, 0, 0, 0 }
430 };
431 static const ul_excl_t excl[] = {
432 { 'a', 'l', 'u' },
433 { 's', 't', OPT_DATE },
434 };
435 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
436
437 setlocale(LC_ALL, "");
438 bindtextdomain(PACKAGE, LOCALEDIR);
439 textdomain(PACKAGE);
440 atexit(close_stdout);
441
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);
445 switch (t) {
446 case 'A':
447 /* for better compatibility with hwclock */
448 ctl.adjfile = optarg;
449 break;
450 case 'a':
451 ctl.clock_mode = CM_AUTO;
452 break;
453 case 'd':
454 devname = optarg;
455 break;
456 case 'l':
457 ctl.clock_mode = CM_LOCAL;
458 break;
459
460 case OPT_LIST:
461 list_modes(&ctl);
462 return EXIT_SUCCESS;
463
464 case 'm':
465 if ((suspend = get_rtc_mode(&ctl, optarg)) < 0)
466 errx(EXIT_FAILURE, _("unrecognized suspend state '%s'"), optarg);
467 ctl.mode_str = optarg;
468 break;
469 case 'n':
470 ctl.dryrun = 1;
471 break;
472 case 's':
473 /* alarm time, seconds-to-sleep (relative) */
474 seconds = strtou32_or_err(optarg, _("invalid seconds argument"));
475 break;
476 case 't':
477 /* alarm time, time_t (absolute, seconds since epoc) */
478 alarm = strtou32_or_err(optarg, _("invalid time argument"));
479 break;
480 case OPT_DATE:
481 { /* alarm time, see timestamp format from manual */
482 usec_t p;
483 if (parse_timestamp(optarg, &p) < 0)
484 errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
485 alarm = (time_t) (p / 1000000);
486 break;
487 }
488 case 'u':
489 ctl.clock_mode = CM_UTC;
490 break;
491 case 'v':
492 ctl.verbose = 1;
493 break;
494 case 'V':
495 printf(UTIL_LINUX_VERSION);
496 exit(EXIT_SUCCESS);
497 case 'h':
498 usage(stdout);
499 default:
500 usage(stderr);
501 }
502 }
503
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;
509 }
510 }
511
512 if (ctl.verbose)
513 printf("%s", ctl.clock_mode == CM_UTC ? _("Using UTC time.\n") :
514 _("Using local time.\n"));
515
516 if (!alarm && !seconds && suspend != DISABLE_MODE && suspend != SHOW_MODE)
517 errx(EXIT_FAILURE, _("must provide wake time (see --seconds, --time and --date options)"));
518
519 /* device must exist and (if we'll sleep) be wakeup-enabled */
520 fd = open_dev_rtc(devname);
521
522 if (suspend != ON_MODE && suspend != NO_MODE && !is_wakeup_enabled(devname))
523 errx(EXIT_FAILURE, _("%s not enabled for wakeup events"), devname);
524
525 /* relative or absolute alarm time, normalized to time_t */
526 if (get_basetimes(&ctl, fd) < 0)
527 exit(EXIT_FAILURE);
528
529 if (ctl.verbose)
530 printf(_("alarm %ld, sys_time %ld, rtc_time %ld, seconds %u\n"),
531 alarm, ctl.sys_time, ctl.rtc_time, seconds);
532
533 if (suspend != DISABLE_MODE && suspend != SHOW_MODE) {
534 /* perform alarm setup when the show or disable modes are not set */
535 if (alarm) {
536 if (alarm < ctl.sys_time)
537 errx(EXIT_FAILURE, _("time doesn't go backward to %s"),
538 ctime(&alarm));
539 alarm += ctl.sys_time - ctl.rtc_time;
540 } else
541 alarm = ctl.rtc_time + seconds + 1;
542
543 if (setup_alarm(&ctl, fd, &alarm) < 0)
544 exit(EXIT_FAILURE);
545
546 if (suspend == NO_MODE || suspend == ON_MODE)
547 printf(_("%s: wakeup using %s at %s"),
548 program_invocation_short_name, devname,
549 ctime(&alarm));
550 else
551 printf(_("%s: wakeup from \"%s\" using %s at %s"),
552 program_invocation_short_name, ctl.mode_str, devname,
553 ctime(&alarm));
554 fflush(stdout);
555 xusleep(10 * 1000);
556 }
557
558 switch (suspend) {
559 case NO_MODE:
560 if (ctl.verbose)
561 printf(_("suspend mode: no; leaving\n"));
562 ctl.dryrun = 1; /* to skip disabling alarm at the end */
563 break;
564 case OFF_MODE:
565 {
566 char *arg[5];
567 int i = 0;
568
569 if (ctl.verbose)
570 printf(_("suspend mode: off; executing %s\n"),
571 _PATH_SHUTDOWN);
572 arg[i++] = _PATH_SHUTDOWN;
573 arg[i++] = "-h";
574 arg[i++] = "-P";
575 arg[i++] = "now";
576 arg[i] = NULL;
577 if (!ctl.dryrun) {
578 execv(arg[0], arg);
579 warn(_("failed to execute %s"), _PATH_SHUTDOWN);
580 rc = EXIT_FAILURE;
581 }
582 break;
583 }
584 case ON_MODE:
585 {
586 unsigned long data;
587
588 if (ctl.verbose)
589 printf(_("suspend mode: on; reading rtc\n"));
590 if (!ctl.dryrun) {
591 do {
592 t = read(fd, &data, sizeof data);
593 if (t < 0) {
594 warn(_("rtc read failed"));
595 break;
596 }
597 if (ctl.verbose)
598 printf("... %s: %03lx\n", devname, data);
599 } while (!(data & RTC_AF));
600 }
601 break;
602 }
603 case DISABLE_MODE:
604 /* just break, alarm gets disabled in the end */
605 if (ctl.verbose)
606 printf(_("suspend mode: disable; disabling alarm\n"));
607 break;
608 case SHOW_MODE:
609 if (ctl.verbose)
610 printf(_("suspend mode: show; printing alarm info\n"));
611 if (print_alarm(&ctl, fd))
612 rc = EXIT_FAILURE;
613 ctl.dryrun = 1; /* don't really disable alarm in the end, just show */
614 break;
615 default:
616 if (ctl.verbose)
617 printf(_("suspend mode: %s; suspending system\n"), ctl.mode_str);
618 sync();
619 suspend_system(&ctl);
620 }
621
622 if (!ctl.dryrun) {
623 struct rtc_wkalrm wake;
624
625 if (ioctl(fd, RTC_WKALM_RD, &wake) < 0) {
626 warn(_("read rtc alarm failed"));
627 rc = EXIT_FAILURE;
628 } else {
629 wake.enabled = 0;
630 if (ioctl(fd, RTC_WKALM_SET, &wake) < 0) {
631 warn(_("disable rtc alarm interrupt failed"));
632 rc = EXIT_FAILURE;
633 }
634 }
635 }
636
637 close(fd);
638 return rc;
639 }