root/lib/boot-time-aux.h

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


DEFINITIONS

This source file includes following definitions.
  1. get_linux_uptime
  2. get_linux_boot_time_fallback
  3. get_linux_boot_time_final_fallback
  4. get_android_boot_time
  5. get_openbsd_boot_time
  6. get_bsd_boot_time_final_fallback
  7. get_haiku_boot_time
  8. get_haiku_boot_time_final_fallback
  9. get_windows_boot_time

     1 /* Auxiliary functions for determining the time when the machine last booted.
     2    Copyright (C) 2023 Free Software Foundation, Inc.
     3 
     4    This file is free software: you can redistribute it and/or modify
     5    it under the terms of the GNU General Public License as published
     6    by the Free Software Foundation, either version 3 of the License,
     7    or (at your option) any later version.
     8 
     9    This file is distributed in the hope that it will be useful,
    10    but WITHOUT ANY WARRANTY; without even the implied warranty of
    11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12    GNU General Public License for more details.
    13 
    14    You should have received a copy of the GNU General Public License
    15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
    16 
    17 /* Written by Bruno Haible <bruno@clisp.org>.  */
    18 
    19 #define SIZEOF(a) (sizeof(a)/sizeof(a[0]))
    20 
    21 #if defined __linux__ || defined __ANDROID__
    22 
    23 /* Store the uptime counter, as managed by the Linux kernel, in *P_UPTIME.
    24    Return 0 upon success, -1 upon failure.  */
    25 _GL_ATTRIBUTE_MAYBE_UNUSED
    26 static int
    27 get_linux_uptime (struct timespec *p_uptime)
    28 {
    29   /* The clock_gettime facility returns the uptime with a resolution of 1 µsec.
    30      It is available with glibc >= 2.14, Android, or musl libc.
    31      In glibc < 2.17 it required linking with librt.  */
    32 # if !defined __GLIBC__ || 2 < __GLIBC__ + (17 <= __GLIBC_MINOR__)
    33   if (clock_gettime (CLOCK_BOOTTIME, p_uptime) >= 0)
    34     return 0;
    35 # endif
    36 
    37   /* /proc/uptime contains the uptime with a resolution of 0.01 sec.
    38      But it does not have read permissions on Android.  */
    39 # if !defined __ANDROID__
    40   FILE *fp = fopen ("/proc/uptime", "re");
    41   if (fp != NULL)
    42     {
    43       char buf[32 + 1];
    44       size_t n = fread (buf, 1, sizeof (buf) - 1, fp);
    45       fclose (fp);
    46       if (n > 0)
    47         {
    48           buf[n] = '\0';
    49           /* buf now contains two values: the uptime and the idle time.  */
    50           time_t s = 0;
    51           char *p;
    52           for (p = buf; '0' <= *p && *p <= '9'; p++)
    53             s = 10 * s + (*p - '0');
    54           if (buf < p)
    55             {
    56               long ns = 0;
    57               if (*p++ == '.')
    58                 for (int i = 0; i < 9; i++)
    59                   ns = 10 * ns + ('0' <= *p && *p <= '9' ? *p++ - '0' : 0);
    60               p_uptime->tv_sec = s;
    61               p_uptime->tv_nsec = ns;
    62               return 0;
    63             }
    64         }
    65     }
    66 # endif
    67 
    68 # if HAVE_DECL_SYSINFO /* not available in Android API < 9 */
    69   /* The sysinfo call returns the uptime with a resolution of 1 sec only.  */
    70   struct sysinfo info;
    71   if (sysinfo (&info) >= 0)
    72     {
    73       p_uptime->tv_sec = info.uptime;
    74       p_uptime->tv_nsec = 0;
    75       return 0;
    76     }
    77 # endif
    78 
    79   return -1;
    80 }
    81 
    82 #endif
    83 
    84 #if defined __linux__ && !defined __ANDROID__
    85 
    86 static int
    87 get_linux_boot_time_fallback (struct timespec *p_boot_time)
    88 {
    89   /* On Alpine Linux, UTMP_FILE is not filled.  It is always empty.
    90      So, get the time stamp of a file that gets touched only during the
    91      boot process.  */
    92 
    93   const char * const boot_touched_files[] =
    94     {
    95       "/var/lib/systemd/random-seed", /* seen on distros with systemd */
    96       "/var/run/utmp",                /* seen on distros with OpenRC */
    97       "/var/lib/random-seed"          /* seen on older distros */
    98     };
    99   for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++)
   100     {
   101       const char *filename = boot_touched_files[i];
   102       struct stat statbuf;
   103       if (stat (filename, &statbuf) >= 0)
   104         {
   105           *p_boot_time = get_stat_mtime (&statbuf);
   106           return 0;
   107         }
   108     }
   109   return -1;
   110 }
   111 
   112 /* The following approach is only usable as a fallback, because it is of
   113    the form
   114      boot_time = (time now) - (kernel's ktime_get_boottime[_ts64] ())
   115    and therefore produces wrong values after the date has been bumped in the
   116    running system, which happens frequently if the system is running in a
   117    virtual machine and this VM has been put into "saved" or "sleep" state
   118    and then resumed.  */
   119 static int
   120 get_linux_boot_time_final_fallback (struct timespec *p_boot_time)
   121 {
   122   struct timespec uptime;
   123   if (get_linux_uptime (&uptime) >= 0)
   124     {
   125       struct timespec result;
   126 # if !defined __GLIBC__ || 2 < __GLIBC__ + (16 <= __GLIBC_MINOR__)
   127       /* Better than:
   128       if (0 <= clock_gettime (CLOCK_REALTIME, &result))
   129          because timespec_get does not need -lrt in glibc 2.16.
   130       */
   131       if (! timespec_get (&result, TIME_UTC))
   132         return -1;
   133 #  else
   134       /* Fall back on lower-res approach that does not need -lrt.
   135          This is good enough; on these hosts UPTIME is even lower-res.  */
   136       struct timeval tv;
   137       int r = gettimeofday (&tv, NULL);
   138       if (r < 0)
   139         return r;
   140       result.tv_sec = tv.tv_sec;
   141       result.tv_nsec = tv.tv_usec * 1000;
   142 #  endif
   143 
   144       if (result.tv_nsec < uptime.tv_nsec)
   145         {
   146           result.tv_nsec += 1000000000;
   147           result.tv_sec -= 1;
   148         }
   149       result.tv_sec -= uptime.tv_sec;
   150       result.tv_nsec -= uptime.tv_nsec;
   151       *p_boot_time = result;
   152       return 0;
   153     }
   154   return -1;
   155 }
   156 
   157 #endif
   158 
   159 #if defined __ANDROID__
   160 
   161 static int
   162 get_android_boot_time (struct timespec *p_boot_time)
   163 {
   164   /* On Android, there is no /var, and normal processes don't have access
   165      to system files.  Therefore use the kernel's uptime counter, although
   166      it produces wrong values after the date has been bumped in the running
   167      system.  */
   168   struct timespec uptime;
   169   if (get_linux_uptime (&uptime) >= 0)
   170     {
   171       struct timespec result;
   172       if (clock_gettime (CLOCK_REALTIME, &result) >= 0)
   173         {
   174           if (result.tv_nsec < uptime.tv_nsec)
   175             {
   176               result.tv_nsec += 1000000000;
   177               result.tv_sec -= 1;
   178             }
   179           result.tv_sec -= uptime.tv_sec;
   180           result.tv_nsec -= uptime.tv_nsec;
   181           *p_boot_time = result;
   182           return 0;
   183         }
   184     }
   185   return -1;
   186 }
   187 
   188 #endif
   189 
   190 #if defined __OpenBSD__
   191 
   192 static int
   193 get_openbsd_boot_time (struct timespec *p_boot_time)
   194 {
   195   /* On OpenBSD, UTMP_FILE is not filled.  It contains only dummy entries.
   196      So, get the time stamp of a file that gets touched only during the
   197      boot process.  */
   198   const char * const boot_touched_files[] =
   199     {
   200       "/var/db/host.random",
   201       "/var/run/utmp"
   202     };
   203   for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++)
   204     {
   205       const char *filename = boot_touched_files[i];
   206       struct stat statbuf;
   207       if (stat (filename, &statbuf) >= 0)
   208         {
   209           *p_boot_time = get_stat_mtime (&statbuf);
   210           return 0;
   211         }
   212     }
   213   return -1;
   214 }
   215 
   216 #endif
   217 
   218 #if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \
   219     && defined CTL_KERN && defined KERN_BOOTTIME \
   220     && !defined __minix
   221 /* macOS, FreeBSD, GNU/kFreeBSD, NetBSD, OpenBSD */
   222 /* On Minix 3.3 this sysctl produces garbage results.  Therefore avoid it.  */
   223 
   224 /* The following approach is only usable as a fallback, because it produces
   225    wrong values after the date has been bumped in the running system, which
   226    happens frequently if the system is running in a virtual machine and this
   227    VM has been put into "saved" or "sleep" state and then resumed.  */
   228 static int
   229 get_bsd_boot_time_final_fallback (struct timespec *p_boot_time)
   230 {
   231   static int request[2] = { CTL_KERN, KERN_BOOTTIME };
   232   struct timeval result;
   233   size_t result_len = sizeof result;
   234 
   235   if (sysctl (request, 2, &result, &result_len, NULL, 0) >= 0)
   236     {
   237       p_boot_time->tv_sec = result.tv_sec;
   238       p_boot_time->tv_nsec = result.tv_usec * 1000;
   239       return 0;
   240     }
   241   return -1;
   242 }
   243 
   244 #endif
   245 
   246 #if defined __HAIKU__
   247 
   248 static int
   249 get_haiku_boot_time (struct timespec *p_boot_time)
   250 {
   251   /* On Haiku, /etc/utmp does not exist.  During boot,
   252        1. the current time is restored, but possibly with a wrong time zone,
   253           that is, with an offset of a few hours,
   254        2. some symlinks and files get created,
   255        3. the various devices are brought up, in particular the network device,
   256        4. the correct date and time is set,
   257        5. some more device nodes get created.
   258      The boot time can be retrieved by looking at a directory created during
   259      phase 5, such as /dev/input.  */
   260   const char * const boot_touched_file = "/dev/input";
   261   struct stat statbuf;
   262   if (stat (boot_touched_file, &statbuf) >= 0)
   263     {
   264       *p_boot_time = get_stat_mtime (&statbuf);
   265       return 0;
   266     }
   267   return -1;
   268 }
   269 
   270 #endif
   271 
   272 #if HAVE_OS_H /* BeOS, Haiku */
   273 
   274 /* The following approach is only usable as a fallback, because it produces
   275    wrong values after the date has been bumped in the running system, which
   276    happens frequently if the system is running in a virtual machine and this
   277    VM has been put into "saved" or "sleep" state and then resumed.  */
   278 static int
   279 get_haiku_boot_time_final_fallback (struct timespec *p_boot_time)
   280 {
   281   system_info si;
   282 
   283   get_system_info (&si);
   284   p_boot_time->tv_sec = si.boot_time / 1000000;
   285   p_boot_time->tv_nsec = (si.boot_time % 1000000) * 1000;
   286   return 0;
   287 }
   288 
   289 #endif
   290 
   291 #if defined __CYGWIN__ || defined _WIN32
   292 
   293 static int
   294 get_windows_boot_time (struct timespec *p_boot_time)
   295 {
   296   /* On Cygwin, /var/run/utmp is empty.
   297      On native Windows, <utmpx.h> and <utmp.h> don't exist.
   298      Instead, on Windows, the boot time can be retrieved by looking at the
   299      time stamp of a file that (normally) gets touched only during the boot
   300      process, namely C:\pagefile.sys.  */
   301   const char * const boot_touched_file =
   302     #if defined __CYGWIN__ && !defined _WIN32
   303     "/cygdrive/c/pagefile.sys"
   304     #else
   305     "C:\\pagefile.sys"
   306     #endif
   307     ;
   308   struct stat statbuf;
   309   if (stat (boot_touched_file, &statbuf) >= 0)
   310     {
   311       *p_boot_time = get_stat_mtime (&statbuf);
   312       return 0;
   313     }
   314   return -1;
   315 }
   316 
   317 #endif

/* [<][>][^][v][top][bottom][index][help] */