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