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