(root)/
util-linux-2.39/
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/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  
      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  
     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  
     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   = %"PRId64"\n", (int64_t) 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 = %"PRId64", (UTC) %s",
     204  				(int64_t) ctl->sys_time, asctime_r(&tm, s));
     205  		gmtime_r(&ctl->rtc_time, &tm);
     206  		printf("\trtctime = %"PRId64", (UTC) %s",
     207  				(int64_t) ctl->rtc_time, asctime_r(&tm, s));
     208  	}
     209  	return 0;
     210  }
     211  
     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  
     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  
     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  
     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  
     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  
     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  
     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  
     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  
     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  
     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  	int suspend = SYSFS_MODE;
     432  	int rc = EXIT_SUCCESS;
     433  	int t;
     434  	int fd;
     435  	time_t alarm = 0, seconds = 0;
     436  	enum {
     437  		OPT_DATE = CHAR_MAX + 1,
     438  		OPT_LIST
     439  	};
     440  	static const struct option long_options[] = {
     441  		{ "adjfile",	required_argument,	NULL, 'A'      },
     442  		{ "auto",	no_argument,		NULL, 'a'      },
     443  		{ "dry-run",	no_argument,		NULL, 'n'      },
     444  		{ "local",	no_argument,		NULL, 'l'      },
     445  		{ "utc",	no_argument,		NULL, 'u'      },
     446  		{ "verbose",	no_argument,		NULL, 'v'      },
     447  		{ "version",	no_argument,		NULL, 'V'      },
     448  		{ "help",	no_argument,		NULL, 'h'      },
     449  		{ "mode",	required_argument,	NULL, 'm'      },
     450  		{ "device",	required_argument,	NULL, 'd'      },
     451  		{ "seconds",	required_argument,	NULL, 's'      },
     452  		{ "time",	required_argument,	NULL, 't'      },
     453  		{ "date",	required_argument,	NULL, OPT_DATE },
     454  		{ "list-modes",	no_argument,		NULL, OPT_LIST },
     455  		{ NULL, 0, NULL, 0 }
     456  	};
     457  	static const ul_excl_t excl[] = {
     458  		{ 'a', 'l', 'u' },
     459  		{ 's', 't', OPT_DATE },
     460  		{ 0 },
     461  	};
     462  	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
     463  
     464  	setlocale(LC_ALL, "");
     465  	bindtextdomain(PACKAGE, LOCALEDIR);
     466  	textdomain(PACKAGE);
     467  	close_stdout_atexit();
     468  
     469  	while ((t = getopt_long(argc, argv, "A:ahd:lm:ns:t:uVv",
     470  					long_options, NULL)) != EOF) {
     471  		err_exclusive_options(t, long_options, excl, excl_st);
     472  		switch (t) {
     473  		case 'A':
     474  			/* for better compatibility with hwclock */
     475  			ctl.adjfile = optarg;
     476  			break;
     477  		case 'a':
     478  			ctl.clock_mode = CM_AUTO;
     479  			break;
     480  		case 'd':
     481  			devname = optarg;
     482  			break;
     483  		case 'l':
     484  			ctl.clock_mode = CM_LOCAL;
     485  			break;
     486  
     487  		case OPT_LIST:
     488  			list_modes(&ctl);
     489  			return EXIT_SUCCESS;
     490  
     491  		case 'm':
     492  			if ((suspend = get_rtc_mode(&ctl, optarg)) < 0)
     493  				errx(EXIT_FAILURE, _("unrecognized suspend state '%s'"), optarg);
     494  			ctl.mode_str = optarg;
     495  			break;
     496  		case 'n':
     497  			ctl.dryrun = 1;
     498  			break;
     499  		case 's':
     500  			/* alarm time, seconds-to-sleep (relative) */
     501  			seconds = strtotime_or_err(optarg, _("invalid seconds argument"));
     502  			break;
     503  		case 't':
     504  			/* alarm time, time_t (absolute, seconds since epoch) */
     505  			alarm = strtotime_or_err(optarg, _("invalid time argument"));
     506  			break;
     507  		case OPT_DATE:
     508  		{	/* alarm time, see timestamp format from manual */
     509  			usec_t p;
     510  			if (parse_timestamp(optarg, &p) < 0)
     511  				errx(EXIT_FAILURE, _("invalid time value \"%s\""), optarg);
     512  			alarm = (time_t) (p / 1000000);
     513  			break;
     514  		}
     515  		case 'u':
     516  			ctl.clock_mode = CM_UTC;
     517  			break;
     518  		case 'v':
     519  			ctl.verbose = 1;
     520  			break;
     521  
     522  		case 'V':
     523  			print_version(EXIT_SUCCESS);
     524  		case 'h':
     525  			usage();
     526  		default:
     527  			errtryhelp(EXIT_FAILURE);
     528  		}
     529  	}
     530  
     531  	if (ctl.clock_mode == CM_AUTO && read_clock_mode(&ctl) < 0) {
     532  		printf(_("%s: assuming RTC uses UTC ...\n"),  program_invocation_short_name);
     533  		ctl.clock_mode = CM_UTC;
     534  	}
     535  
     536  	if (ctl.verbose)
     537  		printf("%s",  ctl.clock_mode == CM_UTC ? _("Using UTC time.\n") :
     538  				_("Using local time.\n"));
     539  
     540  	if (!alarm && !seconds && suspend != DISABLE_MODE && suspend != SHOW_MODE)
     541  		errx(EXIT_FAILURE, _("must provide wake time (see --seconds, --time and --date options)"));
     542  
     543  	/* device must exist and (if we'll sleep) be wakeup-enabled */
     544  	fd = open_dev_rtc(devname);
     545  
     546  	if (suspend != ON_MODE && suspend != NO_MODE && !is_wakeup_enabled(devname))
     547  		errx(EXIT_FAILURE, _("%s not enabled for wakeup events"), devname);
     548  
     549  	/* relative or absolute alarm time, normalized to time_t */
     550  	if (get_basetimes(&ctl, fd) < 0)
     551  		exit(EXIT_FAILURE);
     552  
     553  	if (ctl.verbose)
     554  		printf(_("alarm %"PRId64", sys_time %"PRId64", "
     555  			 "rtc_time %"PRId64", seconds %"PRIu64"\n"),
     556  				(int64_t) alarm, (int64_t) ctl.sys_time,
     557  				(int64_t) ctl.rtc_time,
     558  				(int64_t) seconds);
     559  
     560  	if (suspend != DISABLE_MODE && suspend != SHOW_MODE) {
     561  		/* perform alarm setup when the show or disable modes are not set */
     562  		if (alarm) {
     563  			if (alarm < ctl.sys_time) {
     564  				char s[CTIME_BUFSIZ];
     565  
     566  				ctime_r(&alarm, s);
     567  				errx(EXIT_FAILURE, _("time doesn't go backward to %s"), s);
     568  			}
     569  			alarm -= ctl.sys_time - ctl.rtc_time;
     570  		} else
     571  			alarm = ctl.rtc_time + seconds + 1;
     572  
     573  		if (setup_alarm(&ctl, fd, &alarm) < 0)
     574  			exit(EXIT_FAILURE);
     575  
     576  		if (suspend == NO_MODE || suspend == ON_MODE) {
     577  			char s[CTIME_BUFSIZ];
     578  
     579  			ctime_r(&alarm, s);
     580  			printf(_("%s: wakeup using %s at %s"),
     581  				program_invocation_short_name, devname, s);
     582  		} else {
     583  			char s[CTIME_BUFSIZ];
     584  
     585  			ctime_r(&alarm, s);
     586  			printf(_("%s: wakeup from \"%s\" using %s at %s"),
     587  				program_invocation_short_name, ctl.mode_str, devname, s);
     588  		}
     589  		fflush(stdout);
     590  		xusleep(10 * 1000);
     591  	}
     592  
     593  	switch (suspend) {
     594  	case NO_MODE:
     595  		if (ctl.verbose)
     596  			printf(_("suspend mode: no; leaving\n"));
     597  		ctl.dryrun = 1;	/* to skip disabling alarm at the end */
     598  		break;
     599  	case OFF_MODE:
     600  	{
     601  		char *arg[5];
     602  		int i = 0;
     603  
     604  		if (!access(_PATH_SHUTDOWN, X_OK)) {
     605  			arg[i++] = _PATH_SHUTDOWN;
     606  			arg[i++] = "-h";
     607  			arg[i++] = "-P";
     608  			arg[i++] = "now";
     609  			arg[i]   = NULL;
     610  		} else if (!access(_PATH_POWEROFF, X_OK)) {
     611  			arg[i++] = _PATH_POWEROFF;
     612  			arg[i]   = NULL;
     613  		} else {
     614  			arg[i] 	 = NULL;
     615  		}
     616  
     617  		if (arg[0]) {
     618  			if (ctl.verbose)
     619  				printf(_("suspend mode: off; executing %s\n"),
     620  						arg[0]);
     621  			if (!ctl.dryrun) {
     622  				execv(arg[0], arg);
     623  				warn(_("failed to execute %s"), arg[0]);
     624  				rc = EX_EXEC_ENOENT;
     625  			}
     626  		} else {
     627  			/* Failed to find shutdown command */
     628  			warn(_("failed to find shutdown command"));
     629  			rc = EX_EXEC_ENOENT;
     630  		}
     631  		break;
     632  	}
     633  	case ON_MODE:
     634  	{
     635  		unsigned long data;
     636  
     637  		if (ctl.verbose)
     638  			printf(_("suspend mode: on; reading rtc\n"));
     639  		if (!ctl.dryrun) {
     640  			do {
     641  				t = read(fd, &data, sizeof data);
     642  				if (t < 0) {
     643  					warn(_("rtc read failed"));
     644  					break;
     645  				}
     646  				if (ctl.verbose)
     647  					printf("... %s: %03lx\n", devname, data);
     648  			} while (!(data & RTC_AF));
     649  		}
     650  		break;
     651  	}
     652  	case DISABLE_MODE:
     653  		/* just break, alarm gets disabled in the end */
     654  		if (ctl.verbose)
     655  			printf(_("suspend mode: disable; disabling alarm\n"));
     656  		break;
     657  	case SHOW_MODE:
     658  		if (ctl.verbose)
     659  			printf(_("suspend mode: show; printing alarm info\n"));
     660  		if (print_alarm(&ctl, fd))
     661  			rc = EXIT_FAILURE;
     662  		ctl.dryrun = 1;	/* don't really disable alarm in the end, just show */
     663  		break;
     664  	default:
     665  		if (ctl.verbose)
     666  			printf(_("suspend mode: %s; suspending system\n"), ctl.mode_str);
     667  		sync();
     668  		suspend_system(&ctl);
     669  	}
     670  
     671  	if (!ctl.dryrun) {
     672  		struct rtc_wkalrm wake;
     673  
     674  		if (ioctl(fd, RTC_WKALM_RD, &wake) < 0) {
     675  			warn(_("read rtc alarm failed"));
     676  			rc = EXIT_FAILURE;
     677  		} else {
     678  			wake.enabled = 0;
     679  			if (ioctl(fd, RTC_WKALM_SET, &wake) < 0) {
     680  				warn(_("disable rtc alarm interrupt failed"));
     681  				rc = EXIT_FAILURE;
     682  			}
     683  		}
     684  	}
     685  
     686  	close(fd);
     687  	return rc;
     688  }