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