root/lib/boot-time.c

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

DEFINITIONS

This source file includes following definitions.
  1. get_boot_time_uncached
  2. get_boot_time
  3. get_boot_time

     1 /* Determine 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 #include <config.h>
    20 
    21 /* Specification.  */
    22 #include "boot-time.h"
    23 
    24 #include <stddef.h>
    25 #include <stdio.h>
    26 #include <string.h>
    27 #include <sys/types.h>
    28 #include <sys/stat.h>
    29 
    30 #if defined __linux__ || defined __ANDROID__
    31 # include <sys/sysinfo.h>
    32 # include <time.h>
    33 #endif
    34 
    35 #if HAVE_SYS_SYSCTL_H && !(defined __GLIBC__ && defined __linux__) && !defined __minix
    36 # if HAVE_SYS_PARAM_H
    37 #  include <sys/param.h>
    38 # endif
    39 # include <sys/sysctl.h>
    40 #endif
    41 
    42 #if HAVE_OS_H
    43 # include <OS.h>
    44 #endif
    45 
    46 #include "idx.h"
    47 #include "readutmp.h"
    48 #include "stat-time.h"
    49 
    50 /* Each of the FILE streams in this file is only used in a single thread.  */
    51 #include "unlocked-io.h"
    52 
    53 /* Some helper functions.  */
    54 #include "boot-time-aux.h"
    55 
    56 /* The following macros describe the 'struct UTMP_STRUCT_NAME',
    57    *not* 'struct gl_utmp'.  */
    58 #undef UT_USER
    59 
    60 /* Accessor macro for the member named ut_user or ut_name.  */
    61 #if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_NAME \
    62      : HAVE_UTMP_H && HAVE_STRUCT_UTMP_UT_NAME)
    63 # define UT_USER(UT) ((UT)->ut_name)
    64 #else
    65 # define UT_USER(UT) ((UT)->ut_user)
    66 #endif
    67 
    68 #if !HAVE_UTMPX_H && HAVE_UTMP_H && defined UTMP_NAME_FUNCTION
    69 # if !HAVE_DECL_ENDUTENT /* Android */
    70 void endutent (void);
    71 # endif
    72 #endif
    73 
    74 #if defined __linux__ || HAVE_UTMPX_H || HAVE_UTMP_H || defined __CYGWIN__ || defined _WIN32
    75 
    76 static int
    77 get_boot_time_uncached (struct timespec *p_boot_time)
    78 {
    79   struct timespec found_boot_time = {0};
    80 
    81 # if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE)
    82 
    83   /* Try to find the boot time in the /var/run/utmp file.  */
    84 
    85 #  if defined UTMP_NAME_FUNCTION /* glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, IRIX, Solaris, Cygwin, Android */
    86 
    87   /* Ignore the return value for now.
    88      Solaris' utmpname returns 1 upon success -- which is contrary
    89      to what the GNU libc version does.  In addition, older GNU libc
    90      versions are actually void.   */
    91   UTMP_NAME_FUNCTION ((char *) UTMP_FILE);
    92 
    93   SET_UTMP_ENT ();
    94 
    95 #   if (defined __linux__ && !defined __ANDROID__) || defined __minix
    96   /* Timestamp of the "runlevel" entry, if any.  */
    97   struct timespec runlevel_ts = {0};
    98 #   endif
    99 
   100   void const *entry;
   101 
   102   while ((entry = GET_UTMP_ENT ()) != NULL)
   103     {
   104       struct UTMP_STRUCT_NAME const *ut = (struct UTMP_STRUCT_NAME const *) entry;
   105 
   106       struct timespec ts =
   107         #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV)
   108         { .tv_sec = ut->ut_tv.tv_sec, .tv_nsec = ut->ut_tv.tv_usec * 1000 };
   109         #else
   110         { .tv_sec = ut->ut_time, .tv_nsec = 0 };
   111         #endif
   112 
   113       if (ut->ut_type == BOOT_TIME)
   114         found_boot_time = ts;
   115 
   116 #   if defined __linux__ && !defined __ANDROID__
   117       if (memcmp (UT_USER (ut), "runlevel", strlen ("runlevel") + 1) == 0
   118           && memcmp (ut->ut_line, "~", strlen ("~") + 1) == 0)
   119         runlevel_ts = ts;
   120 #   endif
   121 #   if defined __minix
   122       if (UT_USER (ut)[0] == '\0'
   123           && memcmp (ut->ut_line, "run-level ", strlen ("run-level ")) == 0)
   124         runlevel_ts = ts;
   125 #   endif
   126     }
   127 
   128   END_UTMP_ENT ();
   129 
   130 #   if defined __linux__ && !defined __ANDROID__
   131   /* On Raspbian, which runs on hardware without a real-time clock, during boot,
   132        1. the clock gets set to 1970-01-01 00:00:00,
   133        2. an entry gets written into /var/run/utmp, with ut_type = BOOT_TIME,
   134           ut_user = "reboot", ut_line = "~", time = 1970-01-01 00:00:05 or so,
   135        3. the clock gets set to a correct value through NTP,
   136        4. an entry gets written into /var/run/utmp, with
   137           ut_user = "runlevel", ut_line = "~", time = correct value.
   138      In this case, get the time from the "runlevel" entry.  */
   139 
   140   /* Workaround for Raspbian:  */
   141   if (found_boot_time.tv_sec <= 60 && runlevel_ts.tv_sec != 0)
   142     found_boot_time = runlevel_ts;
   143   if (found_boot_time.tv_sec == 0)
   144     {
   145       /* Workaround for Alpine Linux:  */
   146       get_linux_boot_time_fallback (&found_boot_time);
   147     }
   148 #   endif
   149 
   150 #   if defined __ANDROID__
   151   if (found_boot_time.tv_sec == 0)
   152     {
   153       /* Workaround for Android:  */
   154       get_android_boot_time (&found_boot_time);
   155     }
   156 #   endif
   157 
   158 #   if defined __minix
   159   /* On Minix, during boot,
   160        1. an entry gets written into /var/run/utmp, with ut_type = BOOT_TIME,
   161           ut_user = "", ut_line = "system boot", time = 1970-01-01 00:00:00,
   162        2. an entry gets written into /var/run/utmp, with
   163           ut_user = "", ut_line = "run-level m", time = correct value.
   164      In this case, copy the time from the "run-level m" entry to the
   165      "system boot" entry.  */
   166   if (found_boot_time.tv_sec <= 60 && runlevel_ts.tv_sec != 0)
   167     found_boot_time = runlevel_ts;
   168 #   endif
   169 
   170 #  else /* HP-UX, Haiku */
   171 
   172   FILE *f = fopen (UTMP_FILE, "re");
   173 
   174   if (f != NULL)
   175     {
   176       for (;;)
   177         {
   178           struct UTMP_STRUCT_NAME ut;
   179 
   180           if (fread (&ut, sizeof ut, 1, f) == 0)
   181             break;
   182 
   183           struct timespec ts =
   184             #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV)
   185             { .tv_sec = ut.ut_tv.tv_sec, .tv_nsec = ut.ut_tv.tv_usec * 1000 };
   186             #else
   187             { .tv_sec = ut.ut_time, .tv_nsec = 0 };
   188             #endif
   189 
   190           if (ut.ut_type == BOOT_TIME)
   191             found_boot_time = ts;
   192         }
   193 
   194       fclose (f);
   195     }
   196 
   197 #  endif
   198 
   199 #  if defined __linux__ && !defined __ANDROID__
   200   if (found_boot_time.tv_sec == 0)
   201     {
   202       get_linux_boot_time_final_fallback (&found_boot_time);
   203     }
   204 #  endif
   205 
   206 # else /* old FreeBSD, OpenBSD, native Windows */
   207 
   208 #  if defined __OpenBSD__
   209   /* Workaround for OpenBSD:  */
   210   get_openbsd_boot_time (&found_boot_time);
   211 #  endif
   212 
   213 # endif
   214 
   215 # if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \
   216      && defined CTL_KERN && defined KERN_BOOTTIME \
   217      && !defined __minix
   218   if (found_boot_time.tv_sec == 0)
   219     {
   220       get_bsd_boot_time_final_fallback (&found_boot_time);
   221     }
   222 # endif
   223 
   224 # if defined __HAIKU__
   225   if (found_boot_time.tv_sec == 0)
   226     {
   227       get_haiku_boot_time (&found_boot_time);
   228     }
   229 # endif
   230 
   231 # if HAVE_OS_H
   232   if (found_boot_time.tv_sec == 0)
   233     {
   234       get_haiku_boot_time_final_fallback (&found_boot_time);
   235     }
   236 # endif
   237 
   238 # if defined __CYGWIN__ || defined _WIN32
   239   if (found_boot_time.tv_sec == 0)
   240     {
   241       /* Workaround for Windows:  */
   242       get_windows_boot_time (&found_boot_time);
   243     }
   244 # endif
   245 
   246   if (found_boot_time.tv_sec != 0)
   247     {
   248       *p_boot_time = found_boot_time;
   249       return 0;
   250     }
   251   else
   252     return -1;
   253 }
   254 
   255 int
   256 get_boot_time (struct timespec *p_boot_time)
   257 {
   258   /* Cache the result from get_boot_time_uncached.  */
   259   static int volatile cached_result = -1;
   260   static struct timespec volatile cached_boot_time;
   261 
   262   if (cached_result < 0)
   263     {
   264       struct timespec boot_time;
   265       int result = get_boot_time_uncached (&boot_time);
   266       cached_boot_time = boot_time;
   267       cached_result = result;
   268     }
   269 
   270   if (cached_result == 0)
   271     {
   272       *p_boot_time = cached_boot_time;
   273       return 0;
   274     }
   275   else
   276     return -1;
   277 }
   278 
   279 #else
   280 
   281 int
   282 get_boot_time (struct timespec *p_boot_time)
   283 {
   284   return -1;
   285 }
   286 
   287 #endif

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