[newlib-cygwin/main] Cygwin: clocks: read leap secs from /etc/leapsecs on older systems

Corinna Vinschen via Cygwin-cvs <[email protected]> Fri, 18 Jul 2025 12:06:43 +0000 (GMT)
Newsgroups gmane.os.cygwin.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=a0634931f181e9b46818f6fda15d6c28583dccbb

commit a0634931f181e9b46818f6fda15d6c28583dccbb
Author:     Corinna Vinschen <[email protected]>
AuthorDate: Fri Jul 18 14:06:16 2025 +0200
Commit:     Corinna Vinschen <[email protected]>
CommitDate: Fri Jul 18 14:06:16 2025 +0200

    Cygwin: clocks: read leap secs from /etc/leapsecs on older systems
    
    Systems prior to W10 1803 don't have the leapsecs registry key
    HKLM\SYSTEM\CurrentControlSet\Control\LeapSecondInformation and
    don't handle leap seconds at all.
    
    Given that new leap seconds are a rather seldom, irregular event,
    drop reading from /usr/share/zoneinfo/leapseconds.  Just read the
    same leap second records from a file /etc/leapsecs as stored in
    the LeapSeconds registry value on newer systems instead.
    
    As a sidenote, the code reading from /usr/share/zoneinfo/leapseconds
    was wrong anyway, because it didn't take the timestamps into account.
    Given IERS publishes new leap seconds about 6 months before they
    occur, CLOCK_TAI would have been one second off for up to 6 months.
    
    /etc/leapsecs doesn't exist yet, so we just default to 37 secs.  If
    new leap seconds get provided for newer systems, make sure to provide
    the /etc/leapsecs file as part of the Cygwin distro with identical
    entries.
    
    Fixes: 2abb929f0ad2 ("Cygwin: clocks: Implement CLOCK_TAI")
    Signed-off-by: Corinna Vinschen <[email protected]>

Diff:
---
 winsup/cygwin/clock.cc | 88 ++++++++++++++++++++++----------------------------
 1 file changed, 39 insertions(+), 49 deletions(-)

diff --git a/winsup/cygwin/clock.cc b/winsup/cygwin/clock.cc
index 007657472961..5a4109c7ac03 100644
--- a/winsup/cygwin/clock.cc
+++ b/winsup/cygwin/clock.cc
@@ -1,10 +1,11 @@
 #include "winsup.h"
+#include <unistd.h>
+#include <fcntl.h>
 #include <realtimeapiset.h>
 #include "pinfo.h"
 #include "clock.h"
 #include "miscfuncs.h"
 #include "registry.h"
-#include <stdio.h>
 
 static inline LONGLONG
 system_qpc_tickspersec ()
@@ -66,6 +67,8 @@ struct reg_leap_secs_t
 void inline
 clk_tai_t::init ()
 {
+  size_t size = 0;
+
   /* Avoid a lock/unlock sequence */
   if (leap_secs)
     return;
@@ -91,66 +94,53 @@ clk_tai_t::init ()
      we always ignore the file! */
   if (!reg.error ())
     {
-      size_t size = 0;
-
       if (!NT_SUCCESS (reg.get_binary (L"LeapSeconds", reg_leap_secs,
 				       sizeof reg_leap_secs, size)))
 	{
 	  ReleaseSRWLockExclusive (&leap_lock);
 	  return;
 	}
-
-      size /= sizeof reg_leap_secs[0];
-      for (size_t i = 0; i < size; ++i)
-	{
-	  struct tm tm = { tm_sec: 59,
-			   tm_min: 59,
-			   tm_hour: 23,
-			   tm_mday: reg_leap_secs[i].day,
-			   tm_mon: reg_leap_secs[i].month - 1,
-			   tm_year: reg_leap_secs[i].year - 1900,
-			   tm_wday: 0,
-			   tm_yday: 0,
-			   tm_isdst: 0,
-			   tm_gmtoff: 0,
-			   tm_zone: 0
-			 };
-	  /* Future timestamp?  We're done.  Note that the leap sec is
-	     second 60, therefore <=, not <! */
-	  if (time (NULL) <= timegm (&tm))
-	    break;
-	  leap_secs += reg_leap_secs[i].negative ? -1 : 1;
-	}
     }
+  /* Windows 8.1 and W10 prior to 1803.  When (if) IERS adds new leap secs,
+     we'll create a file /etc/leapsecs in the same format as the aforementioned
+     registry value used on newer OSes. */
   else
     {
-      FILE *fp = fopen ("/usr/share/zoneinfo/leapseconds", "r");
-      if (fp)
+      int fd = open ("/etc/leapsecs", O_RDONLY);
+
+      if (fd < 0)
 	{
-	  char buf[256];
-
-	  leap_secs = 10; /* Leap secs 1972 */
-	  while (fgets (buf, sizeof buf, fp))
-	    {
-	      if (buf[0] != 'L')
-		continue;
-	      if (strlen (buf) < 30)
-		continue;
-	      switch (buf[26])
-		{
-		case '+':
-		  ++leap_secs;
-		  break;
-		case '-':
-		  --leap_secs;
-		  break;
-		default:
-		  break;
-		}
-	    }
-	  fclose (fp);
+	  ReleaseSRWLockExclusive (&leap_lock);
+	  return;
 	}
+      size = (size_t) read (fd, reg_leap_secs, sizeof reg_leap_secs);
+      if ((ssize_t) size < 0)
+	size = 0;
+      close (fd);
+    }
+
+  size /= sizeof reg_leap_secs[0];
+  for (size_t i = 0; i < size; ++i)
+    {
+      struct tm tm = { tm_sec: 59,
+		       tm_min: 59,
+		       tm_hour: 23,
+		       tm_mday: reg_leap_secs[i].day,
+		       tm_mon: reg_leap_secs[i].month - 1,
+		       tm_year: reg_leap_secs[i].year - 1900,
+		       tm_wday: 0,
+		       tm_yday: 0,
+		       tm_isdst: 0,
+		       tm_gmtoff: 0,
+		       tm_zone: 0
+		     };
+      /* Future timestamp?  We're done.  Note that the leap sec is
+	 second 60, therefore <=, not <! */
+      if (time (NULL) <= timegm (&tm))
+	break;
+      leap_secs += reg_leap_secs[i].negative ? -1 : 1;
     }
+
   ReleaseSRWLockExclusive (&leap_lock);
 }