Some of the bringup folks are debugging RTC issues and asked for this.

Rather than duplicate the weird xtzset dance with mktime, I've factored
that out into a new xmktime that takes a boolean for whether to use UTC
or local time.

Otherwise, the slight cleanup of hwclock.c is entirely optional. The
only functional change there is that util-linux 2.34's hwclock uses ISO
time format, which is the usual toybox preference anyway, so I've
switched it over to that rather than ctime(3).

Bug: http://b/152042947
---
 lib/lib.h              |   1 +
 lib/portability.h      |   1 +
 lib/xwrap.c            |  13 ++++
 toys/other/hwclock.c   |  60 ++++++------------
 toys/pending/rtcwake.c | 134 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 169 insertions(+), 40 deletions(-)
 create mode 100644 toys/pending/rtcwake.c
From 3b05e50c172dcb2c116a2eedfdd97e31c9633c72 Mon Sep 17 00:00:00 2001
From: Elliott Hughes <e...@google.com>
Date: Wed, 1 Apr 2020 10:57:30 -0700
Subject: [PATCH] Add rtcwake.

Some of the bringup folks are debugging RTC issues and asked for this.

Rather than duplicate the weird xtzset dance with mktime, I've factored
that out into a new xmktime that takes a boolean for whether to use UTC
or local time.

Otherwise, the slight cleanup of hwclock.c is entirely optional. The
only functional change there is that util-linux 2.34's hwclock uses ISO
time format, which is the usual toybox preference anyway, so I've
switched it over to that rather than ctime(3).

Bug: http://b/152042947
---
 lib/lib.h              |   1 +
 lib/portability.h      |   1 +
 lib/xwrap.c            |  13 ++++
 toys/other/hwclock.c   |  60 ++++++------------
 toys/pending/rtcwake.c | 134 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 169 insertions(+), 40 deletions(-)
 create mode 100644 toys/pending/rtcwake.c

diff --git a/lib/lib.h b/lib/lib.h
index 66b39d67..c528f190 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -188,6 +188,7 @@ void xsignal(int signal, void *handler);
 time_t xvali_date(struct tm *tm, char *str);
 void xparsedate(char *str, time_t *t, unsigned *nano, int endian);
 char *xgetline(FILE *fp, int *len);
+time_t xmktime(struct tm *tm, int utc);
 
 // lib.c
 void verror_msg(char *msg, int err, va_list va);
diff --git a/lib/portability.h b/lib/portability.h
index acc32fd4..41128958 100644
--- a/lib/portability.h
+++ b/lib/portability.h
@@ -175,6 +175,7 @@ void *memmem(const void *haystack, size_t haystack_length,
 // Linux headers not listed by POSIX or LSB
 #include <sys/mount.h>
 #ifdef __linux__
+#include <linux/rtc.h>
 #include <sys/statfs.h>
 #include <sys/swap.h>
 #include <sys/sysinfo.h>
diff --git a/lib/xwrap.c b/lib/xwrap.c
index 555fbd5e..5f18f295 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -1077,3 +1077,16 @@ char *xgetline(FILE *fp, int *len)
 
   return new;
 }
+
+time_t xmktime(struct tm *tm, int utc)
+{
+  char *old_tz = utc ? xtzset("UTC0") : 0;
+  time_t result;
+
+  if ((result = mktime(tm)) < 0) error_exit("mktime");
+  if (utc) {
+    free(xtzset(old_tz));
+    free(old_tz);
+  }
+  return result;
+}
diff --git a/toys/other/hwclock.c b/toys/other/hwclock.c
index 52e71604..5ab308df 100644
--- a/toys/other/hwclock.c
+++ b/toys/other/hwclock.c
@@ -25,12 +25,9 @@ config HWCLOCK
 
 #define FOR_hwclock
 #include "toys.h"
-#include <linux/rtc.h>
 
 GLOBALS(
   char *f;
-
-  int utc;
 )
 
 static int rtc_find(struct dirtree* node)
@@ -61,23 +58,17 @@ void hwclock_main()
   struct timezone tzone;
   struct timeval timeval;
   struct tm tm;
-  time_t time;
-  int fd = -1;
+  int fd = -1, utc;
 
-  // check for Grenich Mean Time
-  if (toys.optflags & FLAG_u) TT.utc = 1;
+  if (FLAG(u)) utc = 1;
+  else if (FLAG(l)) utc = 0;
   else {
-    FILE *fp;
-    char *s = 0;
-
-    for (fp = fopen("/etc/adjtime", "r");
-         fp && getline(&s, (void *)toybuf, fp)>0;
-         free(s), s = 0) TT.utc += !strncmp(s, "UTC", 3);
-    if (fp) fclose(fp);
+    xreadfile("/etc/adjtime", toybuf, sizeof(toybuf));
+    utc = !!strstr(toybuf, "UTC");
   }
 
-  if (!(toys.optflags&FLAG_t)) {
-    int w = toys.optflags & FLAG_w, flag = O_WRONLY*w;
+  if (!FLAG(t)) {
+    int flag = O_WRONLY*FLAG(w);
 
     // Open /dev/rtc (if your system has no /dev/rtc symlink, search for it).
     if (!TT.f && (fd = open("/dev/rtc", flag)) == -1) {
@@ -87,49 +78,38 @@ void hwclock_main()
     if (fd == -1) fd = xopen(TT.f, flag);
 
     // Get current time in seconds from rtc device. todo: get subsecond time
-    if (!w) {
-      char *s = s;
-
+    if (!FLAG(w)) {
       xioctl(fd, RTC_RD_TIME, &tm);
-      if (TT.utc) s = xtzset("UTC0");
-      if ((time = mktime(&tm)) < 0) error_exit("mktime failed");
-      if (TT.utc) {
-        free(xtzset(s));
-        free(s);
-      }
+      timeval.tv_sec = xmktime(&tm, utc);
+      timeval.tv_usec = 0; // todo: fixit
     }
   }
 
-  if (toys.optflags & (FLAG_w|FLAG_t)) {
+  if (FLAG(w) || FLAG(t)) {
     if (gettimeofday(&timeval, 0)) perror_exit("gettimeofday failed");
-    if (!(TT.utc ? gmtime_r : localtime_r)(&timeval.tv_sec, &tm))
-      error_exit(TT.utc ? "gmtime_r failed" : "localtime_r failed");
+    if (!(utc ? gmtime_r : localtime_r)(&timeval.tv_sec, &tm))
+      error_exit(utc ? "gmtime_r failed" : "localtime_r failed");
   }
 
-  if (toys.optflags & FLAG_w) {
+  if (FLAG(w)) {
     /* The value of tm_isdst is positive if daylight saving time is in effect,
      * zero if it is not and negative if the information is not available. 
      * todo: so why isn't this negative...? */
     tm.tm_isdst = 0;
     xioctl(fd, RTC_SET_TIME, &tm);
-  } else if (toys.optflags & FLAG_s) {
+  } else if (FLAG(s)) {
     tzone.tz_minuteswest = timezone / 60 - 60 * daylight;
-    timeval.tv_sec = time;
-    timeval.tv_usec = 0; // todo: fixit
-  } else if (toys.optflags & FLAG_t) {
+  } else if (FLAG(t)) {
     // Adjust seconds for timezone and daylight saving time
     // extern long timezone is defined in header sys/time.h
     tzone.tz_minuteswest = timezone / 60;
     if (tm.tm_isdst) tzone.tz_minuteswest -= 60;
-    if (!TT.utc) timeval.tv_sec += tzone.tz_minuteswest * 60;
+    if (!utc) timeval.tv_sec += tzone.tz_minuteswest * 60;
   } else {
-    char *c = ctime(&time), *s = strrchr(c, '\n');
-
-    if (s) *s = '\0';
-    // TODO: implement this.
-    xprintf("%s  0.000000 seconds\n", c);
+    strftime(toybuf, sizeof(toybuf), "%F %T%z", &tm);
+    xputs(toybuf);
   }
-  if (toys.optflags & (FLAG_t|FLAG_s)) {
+  if (FLAG(t) || FLAG(s)) {
     tzone.tz_dsttime = 0;
     if (settimeofday(&timeval, &tzone)) perror_exit("settimeofday failed");
   }
diff --git a/toys/pending/rtcwake.c b/toys/pending/rtcwake.c
new file mode 100644
index 00000000..0929d51f
--- /dev/null
+++ b/toys/pending/rtcwake.c
@@ -0,0 +1,134 @@
+/* rtcwake.c - enter sleep state until given time.
+ *
+ * Copyright 2020 The Android Open Source Project
+
+USE_RTCWAKE(NEWTOY(rtcwake, "(list-modes);(auto)a(device)d:(local)l(mode)m:(seconds)s#(time)t#(utc)u(verbose)v[!alu]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config RTCWAKE
+  bool "rtcwake"
+  default n
+  help
+    usage: rtcwake [-aluv] [-d FILE] [-m MODE] [-s SECS] [-t UNIX]
+
+    Enter the given sleep state until the given time.
+
+    -a	RTC uses time specified in /etc/adjtime
+    -d FILE	Device to use (default /dev/rtc)
+    -l	RTC uses local time
+    -m	Mode (--list-modes to see those supported by your kernel):
+    	  disable  Cancel current alarm
+    	  freeze   Freeze processes, idle processors
+    	  disk     S4: suspend to disk
+    	  mem      S3: suspend to RAM
+    	  no       Don't suspend, just set wakeup time
+    	  off      S5: power off
+    	  on       Don't suspend, poll RTC for alarm
+    	  show     Don't suspend, just show current alarm
+    	  standby  S1: default
+    -s SECS	Wake SECS seconds from now
+    -t UNIX	Wake UNIX seconds from epoch
+    -u	RTC uses UTC
+    -v	Verbose
+*/
+
+#define FOR_rtcwake
+#include "toys.h"
+
+GLOBALS(
+  long long t, s;
+  char *m, *d;
+
+  struct rtc_wkalrm alarm;
+)
+
+void rtcwake_main(void)
+{
+  time_t now, rtc_now, then;
+  struct tm rtc_tm;
+  int fd, utc;
+
+  if (FLAG(list_modes)) {
+    xreadfile("/sys/power/state", toybuf, sizeof(toybuf));
+    printf("off no on disable show %s", toybuf);
+    return;
+  }
+
+  // util-linux defaults to "suspend", even though I don't have anything that
+  // supports that (testing everything from a ~2010 laptop to a 2019 desktop).
+  if (!TT.m) TT.m = "suspend";
+
+  if (FLAG(u)) utc = 1;
+  else if (FLAG(l)) utc = 0;
+  else {
+    xreadfile("/etc/adjtime", toybuf, sizeof(toybuf));
+    utc = !!strstr(toybuf, "UTC");
+  }
+  if (FLAG(v)) xprintf("RTC time: %s\n", utc ? "UTC" : "local");
+
+  if (!TT.d) TT.d = "/dev/rtc0";
+  if (FLAG(v)) xprintf("Device: %s\n", TT.d);
+  fd = xopen(TT.d, O_RDWR);
+
+  now = time(0);
+  xioctl(fd, RTC_RD_TIME, &rtc_tm);
+  rtc_now = xmktime(&rtc_tm, utc);
+  if (FLAG(v)) {
+    xprintf("System time:\t%lld / %s", (long long)now, ctime(&now));
+    xprintf("RTC time:\t%lld / %s", (long long)rtc_now, ctime(&rtc_now));
+  }
+
+  if (!strcmp(TT.m, "show")) { // Don't suspend, just show current alarm.
+    xioctl(fd, RTC_WKALM_RD, &TT.alarm);
+    if (!TT.alarm.enabled) xputs("alarm: off");
+    else {
+      if ((then = mktime((void *)&TT.alarm.time)) < 0) perror_exit("mktime");
+      xprintf("alarm: on %s", ctime(&then));
+    }
+    goto done;
+  } else if (!strcmp(TT.m, "disable")) { // Cancel current alarm.
+    xioctl(fd, RTC_WKALM_RD, &TT.alarm);
+    TT.alarm.enabled = 0;
+    xioctl(fd, RTC_WKALM_SET, &TT.alarm);
+    goto done;
+  }
+
+  if (FLAG(s)) {
+    then = rtc_now + TT.s + 1; // strace shows util-linux adds 1.
+  } else if (FLAG(t)) {
+    then = TT.t + (rtc_now - now);
+    if (then<=rtc_now) error_exit("rtc %lld >= %lld", (long long)rtc_now, TT.t);
+  } else help_exit("-m %s needs -s or -t", TT.m);
+  if (FLAG(v)) xprintf("Wake time:\t%lld / %s", (long long)then, ctime(&then));
+
+  if (!(utc ? gmtime_r : localtime_r)(&then, (void *)&TT.alarm.time))
+    error_exit(utc ? "gmtime_r failed" : "localtime_r failed");
+
+  TT.alarm.enabled = 1;
+  xioctl(fd, RTC_WKALM_SET, &TT.alarm);
+  sync();
+
+  xprintf("wakeup using \"%s\" from %s at %s", TT.m, TT.d, ctime(&then));
+  msleep(10);
+
+  if (!strcmp(TT.m, "no")) { // Don't suspend, just set wakeup time.
+  } else if (!strcmp(TT.m, "on")) { // Don't suspend, poll RTC for alarm.
+    unsigned long data = 0;
+
+    if (FLAG(v)) xputs("Reading RTC...");
+    while (!(data & RTC_AF)) {
+      if (read(fd, &data, sizeof(data)) != sizeof(data)) perror_exit("read");
+      if (FLAG(v)) xprintf("... %s: %lx\n", TT.d, data);
+    }
+  } else if (!strcmp(TT.m, "off")) {
+    xexec((char *[]){"poweroff", 0});
+  } else {
+    // Everything else lands here for one final step. The write will fail with
+    // EINVAL if the mode is not supported.
+    int fd = xopen("/sys/power/state", O_WRONLY);
+
+    xwrite(fd, TT.m, strlen(TT.m));
+    close(fd);
+  }
+done:
+  close(fd);
+}
-- 
2.26.0.rc2.310.g2932bb562d-goog

_______________________________________________
Toybox mailing list
Toybox@lists.landley.net
http://lists.landley.net/listinfo.cgi/toybox-landley.net

Reply via email to