LCOV - code coverage report
Current view: top level - disco/events - fd_boot_report.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 997 1196 83.4 %
Date: 2026-09-17 04:28:31 Functions: 32 33 97.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "fd_boot_report.h"
       3             : 
       4             : #include "../../ballet/pb/fd_pb_encode.h"
       5             : 
       6             : /* manually include just fd_features_generated.h so we can get
       7             :    FD_FEATURE_SET_ID without anything else that we don't need. */
       8             : #define HEADER_fd_src_flamenco_features_fd_features_h
       9             : #include "../../flamenco/features/fd_features_generated.h"
      10             : #undef HEADER_fd_src_flamenco_features_fd_features_h
      11             : 
      12             : #if defined(__x86_64__)
      13             : #include <cpuid.h>
      14             : #endif
      15             : #include <dirent.h>
      16             : #include <errno.h>
      17             : #include <fcntl.h>
      18             : #include <stdio.h>
      19             : #include <stdlib.h>
      20             : #include <string.h>
      21             : #include <time.h>
      22             : #include <unistd.h>
      23             : #include <gnu/libc-version.h>
      24             : #include <linux/ethtool.h>
      25             : #include <linux/sockios.h>
      26             : #include <net/if.h>
      27             : #include <sys/ioctl.h>
      28             : #include <sys/socket.h>
      29             : #include <sys/statvfs.h>
      30             : #include <sys/utsname.h>
      31             : 
      32             : static int
      33             : read_file( char const * path,
      34             :            char *       buf,
      35             :            ulong        buf_sz,
      36        1272 :            ulong *      out_len ) {
      37        1272 :   int fd = open( path, O_RDONLY|O_CLOEXEC );
      38        1272 :   if( FD_UNLIKELY( fd<0 ) ) { buf[ 0 ] = '\0'; if( out_len ) *out_len = 0UL; return -1; }
      39        1011 :   ulong off = 0UL;
      40        2034 :   while( off<buf_sz-1UL ) {
      41        2031 :     long n = read( fd, buf+off, buf_sz-1UL-off );
      42        2031 :     if( FD_UNLIKELY( n<0L ) ) {
      43           0 :       if( FD_LIKELY( errno==EINTR ) ) continue;
      44           0 :       close( fd );
      45           0 :       buf[ 0 ] = '\0'; if( out_len ) *out_len = 0UL;
      46           0 :       return -1;
      47           0 :     }
      48        2031 :     if( !n ) break;
      49        1023 :     off += (ulong)n;
      50        1023 :   }
      51        1011 :   close( fd );
      52        1011 :   buf[ off ] = '\0';
      53        1011 :   if( out_len ) *out_len = off;
      54        1011 :   return 0;
      55        1011 : }
      56             : 
      57             : static int
      58             : read_cstr( char const * path,
      59             :            char *       buf,
      60        1254 :            ulong        buf_sz ) {
      61        1254 :   if( FD_UNLIKELY( read_file( path, buf, buf_sz, NULL ) ) ) return -1;
      62         996 :   ulong n = strlen( buf );
      63        2172 :   while( n && (buf[ n-1UL ]=='\n' || buf[ n-1UL ]=='\r' || buf[ n-1UL ]==' ' || buf[ n-1UL ]=='\t') ) buf[ --n ] = '\0';
      64         996 :   return 0;
      65        1254 : }
      66             : 
      67             : static int
      68             : read_ulong( char const * path,
      69         807 :             ulong *      out ) {
      70         807 :   char buf[ 64 ];
      71         807 :   if( FD_UNLIKELY( read_cstr( path, buf, sizeof(buf) ) ) ) return -1;
      72         780 :   char * end = NULL;
      73         780 :   ulong v = strtoul( buf, &end, 0 );
      74         780 :   if( FD_UNLIKELY( end==buf ) ) return -1;
      75         780 :   *out = v;
      76         780 :   return 0;
      77         780 : }
      78             : 
      79             : static int
      80             : read_long( char const * path,
      81          42 :            long *       out ) {
      82          42 :   char buf[ 64 ];
      83          42 :   if( FD_UNLIKELY( read_cstr( path, buf, sizeof(buf) ) ) ) return -1;
      84          39 :   char * end = NULL;
      85          39 :   long v = strtol( buf, &end, 0 );
      86          39 :   if( FD_UNLIKELY( end==buf ) ) return -1;
      87          39 :   *out = v;
      88          39 :   return 0;
      89          39 : }
      90             : 
      91             : /* variant index lookup: 1-based position in schema variant order, 0 if
      92             :    no match (caller substitutes its unknown/other value) */
      93             : 
      94             : static int
      95             : variant_idx( char const *         s,
      96             :              char const * const * tbl,
      97          12 :              int                  cnt ) {
      98          45 :   for( int i=0; i<cnt; i++ ) if( !strcmp( s, tbl[ i ] ) ) return i+1;
      99           0 :   return 0;
     100          12 : }
     101             : 
     102             : /* build info comes from two blobs in the same line format: the
     103             :    $(OBJDIR)/info file embedded by fd_log (machine target and extras;
     104             :    generated once per build dir so its date and git status go stale on
     105             :    incremental builds), and fd_bin_build_info, captured at the start of
     106             :    every make run and installed by the link of a production binary (see
     107             :    _make-exe).  The latter is weak: NULL in binaries linked without it. */
     108             : 
     109             : extern char const fd_bin_build_info[] __attribute__((weak));
     110             : 
     111             : static char const *
     112             : build_info_line( char const * blob,
     113             :                  char const * key,
     114             :                  char *       out,
     115           9 :                  ulong        out_sz ) {
     116           9 :   out[ 0 ] = '\0';
     117           9 :   char const * p = blob;
     118           9 :   ulong key_len = strlen( key );
     119          24 :   while( *p ) {
     120          24 :     char const * line = p;
     121          24 :     while( *line==' ' ) line++;
     122          24 :     if( !strncmp( line, key, key_len ) ) {
     123           9 :       char const * v = line+key_len;
     124          39 :       while( *v==' ' ) v++;
     125           9 :       ulong n = 0UL;
     126         164 :       while( v[ n ] && v[ n ]!='\n' && n<out_sz-1UL ) { out[ n ] = v[ n ]; n++; }
     127           9 :       out[ n ] = '\0';
     128           9 :       while( n && (out[ n-1UL ]==' '||out[ n-1UL ]=='\r') ) out[ --n ] = '\0';
     129           9 :       return out;
     130           9 :     }
     131        1076 :     while( *p && *p!='\n' ) p++;
     132          15 :     if( *p ) p++;
     133          15 :   }
     134           0 :   return out;
     135           9 : }
     136             : 
     137             : static ulong
     138           3 : build_timestamp_nanos( char const * blob ) {
     139           3 :   char date[ 64 ];
     140           3 :   build_info_line( blob, "# date", date, sizeof(date) );
     141           3 :   struct tm tm = {0};
     142           3 :   int tzh = 0, tzm = 0; char tzs = '+';
     143           3 :   if( FD_UNLIKELY( 8!=sscanf( date, "%d-%d-%d %d:%d:%d %c%2d%2d",
     144           3 :       &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tzs, &tzh, &tzm )
     145           3 :       && 9!=sscanf( date, "%d-%d-%d %d:%d:%d %c%2d%2d",
     146           3 :       &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tzs, &tzh, &tzm ) ) ) return 0UL;
     147           3 :   tm.tm_year -= 1900; tm.tm_mon -= 1;
     148           3 :   time_t t = timegm( &tm );
     149           3 :   if( FD_UNLIKELY( t==(time_t)-1 ) ) return 0UL;
     150           3 :   long off = (long)tzh*3600L + (long)tzm*60L;
     151           3 :   if( tzs=='-' ) off = -off;
     152           3 :   return (ulong)( (long)t - off )*1000000000UL;
     153           3 : }
     154             : 
     155             : static int
     156           3 : build_git_dirty( char const * blob ) {
     157           3 :   char const * p = blob;
     158          27 :   while( *p ) {
     159          24 :     char const * line = p;
     160          24 :     while( *line==' ' ) line++;
     161          24 :     if( line[ 0 ]=='1' || line[ 0 ]=='2' || line[ 0 ]=='u' ) {
     162           0 :       if( line[ 1 ]==' ' ) return 1;
     163           0 :     }
     164        1007 :     while( *p && *p!='\n' ) p++;
     165          24 :     if( *p ) p++;
     166          24 :   }
     167           3 :   return 0;
     168           3 : }
     169             : 
     170             : static void
     171           3 : collect_os( fd_boot_report_t * r ) {
     172           3 :   struct utsname un;
     173           3 :   if( FD_LIKELY( 0==uname( &un ) ) ) {
     174           3 :     FD_TEST( fd_cstr_printf_check( r->kernel_release, sizeof(r->kernel_release), NULL, "%s", un.release ) );
     175           3 :     FD_TEST( fd_cstr_printf_check( r->kernel_version, sizeof(r->kernel_version), NULL, "%s", un.version ) );
     176           3 :   }
     177             : 
     178             :   /* vulnerabilities whose sysfs status reports an active mitigation */
     179           3 :   DIR * vul = opendir( "/sys/devices/system/cpu/vulnerabilities" );
     180           3 :   if( FD_LIKELY( vul ) ) {
     181           3 :     struct dirent * ent;
     182          63 :     while( (ent = readdir( vul )) && r->mitigations_cnt<32UL ) {
     183          60 :       if( ent->d_name[ 0 ]=='.' ) continue;
     184          54 :       char path[ 320 ], status[ 256 ];
     185          54 :       FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/vulnerabilities/%s", ent->d_name ) );
     186          54 :       if( read_cstr( path, status, sizeof(status) ) ) continue;
     187          54 :       if( strncmp( status, "Mitigation", 10UL ) ) continue;
     188          18 :       fd_cstr_printf( r->mitigations[ r->mitigations_cnt++ ], 33UL, NULL, "%s", ent->d_name );
     189          18 :     }
     190           3 :     closedir( vul );
     191           3 :   }
     192             : 
     193           3 :   char osrel[ 8192 ];
     194           3 :   if( FD_LIKELY( !read_file( "/etc/os-release", osrel, sizeof(osrel), NULL ) ) ) {
     195           3 :     char * saveptr = NULL;
     196          60 :     for( char * line = strtok_r( osrel, "\n", &saveptr ); line; line = strtok_r( NULL, "\n", &saveptr ) ) {
     197          57 :       char * eq = strchr( line, '=' );
     198          57 :       if( !eq ) continue;
     199          57 :       *eq = '\0';
     200          57 :       char * val = eq+1;
     201          57 :       ulong vlen = strlen( val );
     202          57 :       if( vlen>=2UL && val[ 0 ]=='"' && val[ vlen-1UL ]=='"' ) { val[ vlen-1UL ] = '\0'; val++; }
     203          57 :       if(      !strcmp( line, "ID"         ) ) fd_cstr_printf( r->distro_id,         sizeof(r->distro_id), NULL, "%s",         val );
     204          54 :       else if( !strcmp( line, "VERSION_ID" ) ) fd_cstr_printf( r->distro_version_id, sizeof(r->distro_version_id), NULL, "%s", val );
     205          51 :       else if( !strcmp( line, "ID_LIKE"    ) ) fd_cstr_printf( r->distro_id_like,    sizeof(r->distro_id_like), NULL, "%s",    val );
     206          57 :     }
     207           3 :   }
     208             : 
     209           3 :   r->libc_kind = 1; /* glibc: the only supported libc */
     210           3 :   fd_cstr_printf( r->libc_version, sizeof(r->libc_version), NULL, "%s", gnu_get_libc_version() );
     211             : 
     212           3 :   static char const * const machine_tbl[] = {
     213           3 :     "native", "linux_gcc_x86_64", "linux_gcc_zen2", "linux_gcc_zen3", "linux_gcc_zen4",
     214           3 :     "linux_gcc_zen5", "linux_gcc_icelake", "linux_clang_x86_64", "linux_clang_haswell",
     215           3 :     "linux_clang_icelake", "linux_clang_zen2", "linux_clang_zen3", "linux_clang_zen4",
     216           3 :     "linux_clang_zen5" };
     217           3 :   char machine[ 64 ];
     218           3 :   build_info_line( fd_log_build_info, "# machine", machine, sizeof(machine) );
     219           3 :   int mt = variant_idx( machine, machine_tbl, 14 );
     220           3 :   r->build_machine_target = mt ? mt : 15; /* other */
     221             : 
     222           3 :   char extras[ 128 ];
     223           3 :   build_info_line( fd_log_build_info, "# extras", extras, sizeof(extras) );
     224           3 :   FD_TEST( fd_cstr_printf_check( r->build_extras, sizeof(r->build_extras), NULL, "%s", extras ) );
     225             : 
     226             :   /* date and dirty state from the link-time blob when this binary has
     227             :      one; the build-dir info file goes stale on incremental builds */
     228           3 :   char const * link_info = fd_bin_build_info ? fd_bin_build_info : fd_log_build_info;
     229           3 :   r->build_git_dirty = build_git_dirty( link_info );
     230             : 
     231           3 : #if defined(__clang__)
     232           3 :   r->compiler = 2;
     233             : #else
     234             :   r->compiler = 1;
     235             : #endif
     236           3 :   fd_cstr_printf( r->compiler_version, sizeof(r->compiler_version), NULL, "%s", __VERSION__ );
     237           3 :   r->build_timestamp_nanos = build_timestamp_nanos( link_info );
     238             : 
     239           3 : #if FD_HAS_SSE
     240           3 :   FD_TEST( fd_cstr_printf_check( r->build_features[ r->build_features_cnt++ ], 8UL, NULL, "%s", "sse" ) );
     241           3 : #endif
     242           3 : #if FD_HAS_AVX
     243           3 :   FD_TEST( fd_cstr_printf_check( r->build_features[ r->build_features_cnt++ ], 8UL, NULL, "%s", "avx" ) );
     244           3 : #endif
     245           1 : #if FD_HAS_AVX512
     246           1 :   FD_TEST( fd_cstr_printf_check( r->build_features[ r->build_features_cnt++ ], 8UL, NULL, "%s", "avx512" ) );
     247           1 : #endif
     248           1 : #if FD_HAS_SHANI
     249           1 :   FD_TEST( fd_cstr_printf_check( r->build_features[ r->build_features_cnt++ ], 8UL, NULL, "%s", "shani" ) );
     250           1 : #endif
     251           2 : #if FD_HAS_AESNI
     252           2 :   FD_TEST( fd_cstr_printf_check( r->build_features[ r->build_features_cnt++ ], 8UL, NULL, "%s", "aesni" ) );
     253           2 : #endif
     254           1 : #if FD_HAS_GFNI
     255           1 :   FD_TEST( fd_cstr_printf_check( r->build_features[ r->build_features_cnt++ ], 8UL, NULL, "%s", "gfni" ) );
     256           1 : #endif
     257           3 : }
     258             : 
     259             : static char const *
     260             : cpuinfo_field( char const * cpuinfo,
     261             :                char const * key,
     262             :                char *       out,
     263          12 :                ulong        out_sz ) {
     264          12 :   out[ 0 ] = '\0';
     265          12 :   char const * p = strstr( cpuinfo, key );
     266          12 :   if( !p ) return out;
     267          12 :   p = strchr( p, ':' );
     268          12 :   if( !p ) return out;
     269          12 :   p++;
     270          48 :   while( *p==' ' || *p=='\t' ) p++;
     271          12 :   ulong n = 0UL;
     272         207 :   while( p[ n ] && p[ n ]!='\n' && n<out_sz-1UL ) { out[ n ] = p[ n ]; n++; }
     273          12 :   out[ n ] = '\0';
     274          12 :   return out;
     275          12 : }
     276             : 
     277             : static void
     278           3 : collect_cpu( fd_boot_report_t * r ) {
     279           3 :   struct utsname un;
     280           3 :   r->cpu_arch = 3; /* unknown */
     281           3 :   if( FD_LIKELY( 0==uname( &un ) ) ) {
     282           3 :     if(      !strcmp ( un.machine, "x86_64"     ) ) r->cpu_arch = 1;
     283           0 :     else if( !strncmp( un.machine, "aarch64", 7 ) || !strncmp( un.machine, "arm", 3 ) ) r->cpu_arch = 2;
     284           3 :   }
     285             : 
     286           3 : # if defined(__x86_64__)
     287           3 :   uint eax, ebx, ecx, edx;
     288           3 :   if( FD_LIKELY( __get_cpuid( 0U, &eax, &ebx, &ecx, &edx ) ) ) {
     289           3 :     char vendor[ 13 ];
     290           3 :     fd_memcpy( vendor,   &ebx, 4UL );
     291           3 :     fd_memcpy( vendor+4, &edx, 4UL );
     292           3 :     fd_memcpy( vendor+8, &ecx, 4UL );
     293           3 :     vendor[ 12 ] = '\0';
     294           3 :     if(      !strcmp( vendor, "GenuineIntel" ) ) r->cpu_vendor = 1;
     295           3 :     else if( !strcmp( vendor, "AuthenticAMD" ) ) r->cpu_vendor = 2;
     296           0 :     else                                         r->cpu_vendor = 3;
     297           3 :   } else r->cpu_vendor = 3;
     298             : 
     299           3 :   if( FD_LIKELY( __get_cpuid( 1U, &eax, &ebx, &ecx, &edx ) ) ) {
     300           3 :     uint base_family = (eax>>8)&0xFU;
     301           3 :     uint ext_family  = (eax>>20)&0xFFU;
     302           3 :     uint base_model  = (eax>>4)&0xFU;
     303           3 :     uint ext_model   = (eax>>16)&0xFU;
     304           3 :     r->cpu_family   = (ushort)( base_family + fd_uint_if( base_family==0xFU, ext_family, 0U ) );
     305           3 :     r->cpu_model_id = (ushort)( base_model | fd_uint_if( base_family==0x6U || base_family==0xFU, ext_model<<4, 0U ) );
     306           3 :     r->cpu_stepping = (uchar)( eax&0xFU );
     307           3 :   }
     308           3 : # endif /* defined(__x86_64__) */
     309             : 
     310           3 :   char cpuinfo[ 16384 ];
     311           3 :   if( FD_LIKELY( !read_file( "/proc/cpuinfo", cpuinfo, sizeof(cpuinfo), NULL ) ) ) {
     312           3 :     char tmp[ 128 ];
     313           3 :     fd_cstr_printf( r->cpu_model_name, sizeof(r->cpu_model_name), NULL, "%s", cpuinfo_field( cpuinfo, "model name", tmp, sizeof(tmp) ) );
     314           3 :     cpuinfo_field( cpuinfo, "microcode", tmp, sizeof(tmp) );
     315           3 :     if( tmp[ 0 ] ) r->cpu_microcode_version = strtoul( tmp, NULL, 0 );
     316           3 :   }
     317             : 
     318             :   /* logical count, physical cores and sockets from the sysfs topology */
     319           3 :   ulong  logical = 0UL;
     320           3 :   ulong  pairs[ 4096 ]; ulong pair_cnt = 0UL; /* (package<<32)|core, deduped */
     321           3 :   ulong  pkgs [ 64 ];   ulong pkg_cnt  = 0UL;
     322         291 :   for( ulong cpu=0UL; cpu<4096UL; cpu++ ) {
     323         291 :     char path[ 128 ];
     324         291 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/topology/physical_package_id", cpu ) );
     325         291 :     ulong pkg;
     326         291 :     if( read_ulong( path, &pkg ) ) {
     327           3 :       FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu", cpu ) );
     328           3 :       if( 0!=access( path, F_OK ) ) break; /* end of cpus */
     329           0 :       continue;                         /* offline cpu */
     330           3 :     }
     331         288 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/topology/core_id", cpu ) );
     332         288 :     ulong core;
     333         288 :     if( read_ulong( path, &core ) ) continue;
     334         288 :     logical++;
     335         288 :     ulong pair = (pkg<<32) | (core&0xFFFFFFFFUL);
     336         288 :     int seen = 0;
     337        7056 :     for( ulong i=0UL; i<pair_cnt; i++ ) if( pairs[ i ]==pair ) { seen = 1; break; }
     338         288 :     if( !seen && pair_cnt<4096UL ) pairs[ pair_cnt++ ] = pair;
     339         288 :     seen = 0;
     340         288 :     for( ulong i=0UL; i<pkg_cnt; i++ ) if( pkgs[ i ]==pkg ) { seen = 1; break; }
     341         288 :     if( !seen && pkg_cnt<64UL ) pkgs[ pkg_cnt++ ] = pkg;
     342         288 :   }
     343           3 :   r->cpu_logical_count       = (ushort)fd_ulong_min( logical,  USHORT_MAX );
     344           3 :   r->cpu_physical_core_count = (ushort)fd_ulong_min( pair_cnt, USHORT_MAX );
     345           3 :   r->cpu_socket_count        = (uchar) fd_ulong_min( pkg_cnt,  UCHAR_MAX );
     346             : 
     347           3 :   ulong v;
     348           3 :   r->smt_active = !read_ulong( "/sys/devices/system/cpu/smt/active", &v ) && v==1UL;
     349             : 
     350           3 :   char buf[ 64 ];
     351           3 :   static char const * const gov_tbl[] = { "performance", "powersave", "ondemand", "conservative", "schedutil", "userspace" };
     352           3 :   r->cpufreq_governor = 7;
     353           3 :   if( !read_cstr( "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", buf, sizeof(buf) ) ) {
     354           3 :     int i = variant_idx( buf, gov_tbl, 6 );
     355           3 :     if( i ) r->cpufreq_governor = i;
     356           3 :   }
     357             : 
     358           3 :   static char const * const drv_tbl[] = { "intel_pstate", "intel_cpufreq", "acpi-cpufreq", "amd-pstate", "amd-pstate-epp", "cppc_cpufreq" };
     359           3 :   r->cpufreq_driver = 7;
     360           3 :   if( !read_cstr( "/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver", buf, sizeof(buf) ) ) {
     361           3 :     int i = variant_idx( buf, drv_tbl, 6 );
     362           3 :     if( i ) r->cpufreq_driver = i;
     363           3 :   }
     364             : 
     365           3 :   static char const * const epp_tbl[] = { "performance", "balance_performance", "balance_power", "power", "default" };
     366           3 :   r->cpu_energy_perf_preference = 6;
     367           3 :   if( !read_cstr( "/sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference", buf, sizeof(buf) ) ) {
     368           0 :     int i = variant_idx( buf, epp_tbl, 5 );
     369           0 :     if( i ) r->cpu_energy_perf_preference = i;
     370           0 :   }
     371             : 
     372           3 :   static char const * const idle_tbl[] = { "intel_idle", "acpi_idle", "none" };
     373           3 :   r->cpu_idle_driver = 4;
     374           3 :   if( !read_cstr( "/sys/devices/system/cpu/cpuidle/current_driver", buf, sizeof(buf) ) ) {
     375           3 :     int i = variant_idx( buf, idle_tbl, 3 );
     376           3 :     if( i ) r->cpu_idle_driver = i;
     377           3 :   }
     378             : 
     379             :   /* deepest idle state not disabled */
     380          12 :   for( ulong s=0UL; s<16UL; s++ ) {
     381          12 :     char path[ 128 ];
     382          12 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu0/cpuidle/state%lu/disable", s ) );
     383          12 :     ulong disabled;
     384          12 :     if( read_ulong( path, &disabled ) ) break;
     385           9 :     if( disabled ) continue;
     386           9 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu0/cpuidle/state%lu/name", s ) );
     387           9 :     read_cstr( path, r->cpu_max_enabled_cstate, sizeof(r->cpu_max_enabled_cstate) );
     388           9 :   }
     389           3 : }
     390             : 
     391             : static void
     392           3 : collect_mem( fd_boot_report_t * r ) {
     393           3 :   char meminfo[ 8192 ];
     394           3 :   if( FD_LIKELY( !read_file( "/proc/meminfo", meminfo, sizeof(meminfo), NULL ) ) ) {
     395           3 :     char tmp[ 64 ];
     396           3 :     cpuinfo_field( meminfo, "MemTotal", tmp, sizeof(tmp) );
     397           3 :     if( tmp[ 0 ] ) r->host_memory_bytes = strtoul( tmp, NULL, 10 )*1024UL;
     398           3 :     cpuinfo_field( meminfo, "MemAvailable", tmp, sizeof(tmp) );
     399           3 :     if( tmp[ 0 ] ) r->host_memory_available_bytes = strtoul( tmp, NULL, 10 )*1024UL;
     400           3 :   }
     401             : 
     402           3 :   ulong max_cpu = 0UL;
     403         195 :   for( ulong node=0UL; node<64UL; node++ ) {
     404         192 :     char path[ 128 ];
     405         192 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/node/node%lu/cpulist", node ) );
     406         192 :     char list[ 16384 ]; /* worst-case alternating cpulist over 1024 cpus */
     407         192 :     if( read_cstr( path, list, sizeof(list) ) ) continue;
     408           3 :     char * saveptr = NULL;
     409           6 :     for( char * range = strtok_r( list, ",", &saveptr ); range; range = strtok_r( NULL, ",", &saveptr ) ) {
     410           3 :       ulong lo, hi;
     411           3 :       if(      2==sscanf( range, "%lu-%lu", &lo, &hi ) ) {}
     412           0 :       else if( 1==sscanf( range, "%lu",     &lo      ) ) hi = lo;
     413           0 :       else continue;
     414         291 :       for( ulong cpu=lo; cpu<=hi && cpu<1024UL; cpu++ ) {
     415         288 :         r->numa_cpu_to_node[ cpu ] = (uchar)fd_ulong_min( node, UCHAR_MAX );
     416         288 :         if( cpu+1UL>max_cpu ) max_cpu = cpu+1UL;
     417         288 :       }
     418           3 :     }
     419           3 :   }
     420           3 :   r->numa_cpu_to_node_cnt = max_cpu;
     421             : 
     422           3 :   char isolated[ 16384 ]; /* worst-case alternating cpulist over 1024 cpus */
     423           3 :   if( !read_cstr( "/sys/devices/system/cpu/isolated", isolated, sizeof(isolated) ) && isolated[ 0 ] ) {
     424           0 :     char * saveptr = NULL;
     425           0 :     for( char * range = strtok_r( isolated, ",", &saveptr ); range; range = strtok_r( NULL, ",", &saveptr ) ) {
     426           0 :       ulong lo, hi;
     427           0 :       if(      2==sscanf( range, "%lu-%lu", &lo, &hi ) ) {}
     428           0 :       else if( 1==sscanf( range, "%lu",     &lo      ) ) hi = lo;
     429           0 :       else continue;
     430           0 :       for( ulong cpu=lo; cpu<=hi && r->isolated_cpus_cnt<1024UL; cpu++ )
     431           0 :         r->isolated_cpus[ r->isolated_cpus_cnt++ ] = (ushort)fd_ulong_min( cpu, USHORT_MAX );
     432           0 :     }
     433           0 :   }
     434           3 : }
     435             : 
     436             : static char const *
     437             : smbios_string( uchar const * raw,
     438             :                ulong         raw_sz,
     439           0 :                uint          idx ) {
     440           0 :   if( !idx || raw_sz<2UL ) return "";
     441           0 :   ulong off = raw[ 1 ]; /* formatted area length */
     442           0 :   for( uint i=1U; off<raw_sz; i++ ) {
     443           0 :     if( i==idx ) return (char const *)( raw+off );
     444           0 :     off += strlen( (char const *)( raw+off ) )+1UL;
     445           0 :     if( off<raw_sz && !raw[ off ] ) break; /* double NUL: end of strings */
     446           0 :   }
     447           0 :   return "";
     448           0 : }
     449             : 
     450             : static void
     451           3 : collect_dimms( fd_boot_report_t * r ) {
     452           3 :   for( ulong i=0UL; i<256UL && r->dmi_dimms_cnt<64UL; i++ ) {
     453           3 :     char path[ 128 ];
     454           3 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/firmware/dmi/entries/17-%lu/raw", i ) );
     455           3 :     uchar raw[ 4096 ];
     456           3 :     ulong raw_sz;
     457           3 :     if( read_file( path, (char *)raw, sizeof(raw), &raw_sz ) ) break;
     458           0 :     if( raw_sz<2UL ) continue;
     459           0 :     ulong fmt_len = raw[ 1 ]; /* formatted area; raw_sz also spans the string table */
     460           0 :     if( fmt_len>raw_sz || fmt_len<0x17UL ) continue; /* need through the speed word */
     461             : 
     462           0 :     ulong size_bytes = 0UL;
     463           0 :     uint  size_w = (uint)raw[ 0x0C ] | ((uint)raw[ 0x0D ]<<8);
     464           0 :     if( size_w==0U || size_w==0xFFFFU ) continue; /* empty or unknown slot */
     465           0 :     if( size_w==0x7FFFU && fmt_len>=0x20UL ) {
     466           0 :       uint ext = (uint)raw[ 0x1C ] | ((uint)raw[ 0x1D ]<<8) | ((uint)raw[ 0x1E ]<<16) | ((uint)raw[ 0x1F ]<<24);
     467           0 :       size_bytes = (ulong)( ext&0x7FFFFFFFU )<<20;
     468           0 :     } else if( size_w&0x8000U ) size_bytes = (ulong)( size_w&0x7FFFU )<<10;
     469           0 :     else                        size_bytes = (ulong)size_w<<20;
     470             : 
     471           0 :     fd_boot_report_dimm_t * d = &r->dmi_dimms[ r->dmi_dimms_cnt++ ];
     472           0 :     d->size_bytes = size_bytes;
     473             : 
     474           0 :     switch( raw[ 0x12 ] ) {
     475           0 :       case 0x12: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "DDR"    ) ); break;
     476           0 :       case 0x13: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "DDR2"   ) ); break;
     477           0 :       case 0x18: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "DDR3"   ) ); break;
     478           0 :       case 0x1A: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "DDR4"   ) ); break;
     479           0 :       case 0x1E: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "LPDDR4" ) ); break;
     480           0 :       case 0x22: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "DDR5"   ) ); break;
     481           0 :       case 0x23: FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "LPDDR5" ) ); break;
     482           0 :       default:   FD_TEST( fd_cstr_printf_check( d->mem_type, sizeof(d->mem_type), NULL, "%s", "other"  ) ); break;
     483           0 :     }
     484             : 
     485           0 :     d->rated_speed_mts = (ulong)raw[ 0x15 ] | ((ulong)raw[ 0x16 ]<<8);
     486           0 :     if( fmt_len>=0x22UL ) d->speed_mts = (ulong)raw[ 0x20 ] | ((ulong)raw[ 0x21 ]<<8);
     487           0 :     if( fmt_len>=0x18UL ) fd_cstr_printf( d->manufacturer, sizeof(d->manufacturer), NULL, "%s", smbios_string( raw, raw_sz, raw[ 0x17 ] ) );
     488           0 :     if( fmt_len>=0x1BUL ) fd_cstr_printf( d->part_number, sizeof(d->part_number), NULL, "%s", smbios_string( raw, raw_sz, raw[ 0x1A ] ) );
     489           0 :     ulong n = strlen( d->part_number );
     490           0 :     while( n && d->part_number[ n-1UL ]==' ' ) d->part_number[ --n ] = '\0';
     491           0 :     n = strlen( d->manufacturer );
     492           0 :     while( n && d->manufacturer[ n-1UL ]==' ' ) d->manufacturer[ --n ] = '\0';
     493           0 :   }
     494           3 : }
     495             : 
     496             : static int
     497             : ethtool_ioc( int          fd,
     498             :              char const * ifname,
     499         150 :              void *       data ) {
     500         150 :   struct ifreq ifr = {0};
     501         150 :   FD_TEST( fd_cstr_printf_check( ifr.ifr_name, IFNAMSIZ, NULL, "%s", ifname ) );
     502         150 :   ifr.ifr_data = data;
     503         150 :   return ioctl( fd, SIOCETHTOOL, &ifr );
     504         150 : }
     505             : 
     506             : static void
     507             : readlink_base( char const * path,
     508             :                char *       out,
     509          27 :                ulong        out_sz ) {
     510          27 :   out[ 0 ] = '\0';
     511          27 :   char target[ PATH_MAX ];
     512          27 :   long n = readlink( path, target, sizeof(target)-1UL );
     513          27 :   if( n<=0L ) return;
     514          24 :   target[ n ] = '\0';
     515          24 :   char * base = strrchr( target, '/' );
     516          24 :   fd_cstr_printf( out, out_sz, NULL, "%s", base ? base+1 : target );
     517          24 : }
     518             : 
     519             : static void
     520             : collect_one_nic( fd_boot_report_t * r,
     521             :                  int                fd,
     522             :                  char const *       name,
     523          15 :                  int                is_bond ) {
     524          15 :   if( r->nic_devices_cnt>=16UL ) return;
     525          15 :   fd_boot_report_nic_t * nic = &r->nic_devices[ r->nic_devices_cnt++ ];
     526          15 :   fd_memset( nic, 0, sizeof(*nic) );
     527          15 :   FD_TEST( fd_cstr_printf_check( nic->name, sizeof(nic->name), NULL, "%s", name ) );
     528          15 :   nic->is_bond_master = is_bond;
     529             : 
     530          15 :   char buf[ 256 ];
     531          15 :   char path[ PATH_MAX ];
     532             : 
     533          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/driver", name ) );
     534          15 :   readlink_base( path, nic->driver, sizeof(nic->driver) );
     535             : 
     536          15 :   struct ethtool_drvinfo drv = { .cmd = ETHTOOL_GDRVINFO };
     537          15 :   if( !ethtool_ioc( fd, name, &drv ) ) {
     538          15 :     if( !nic->driver[ 0 ] ) FD_TEST( fd_cstr_printf_check( nic->driver, sizeof(nic->driver), NULL, "%s", drv.driver ) );
     539          15 :     FD_TEST( fd_cstr_printf_check( nic->driver_version,   sizeof(nic->driver_version), NULL, "%s",   drv.version ) );
     540          15 :     FD_TEST( fd_cstr_printf_check( nic->firmware_version, sizeof(nic->firmware_version), NULL, "%s", drv.fw_version ) );
     541          15 :     FD_TEST( fd_cstr_printf_check( nic->bus_info,         sizeof(nic->bus_info), NULL, "%s",         drv.bus_info ) );
     542          15 :   }
     543             : 
     544          15 :   ulong v;
     545          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/current_link_width", name ) );
     546          15 :   if( !read_ulong( path, &v ) ) nic->pcie_current_link_width = (uchar)fd_ulong_min( v, UCHAR_MAX );
     547          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/max_link_width", name ) );
     548          15 :   if( !read_ulong( path, &v ) ) nic->pcie_max_link_width = (uchar)fd_ulong_min( v, UCHAR_MAX );
     549          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/current_link_speed", name ) );
     550          15 :   if( !read_cstr( path, buf, sizeof(buf) ) ) fd_cstr_printf( nic->pcie_current_link_speed, sizeof(nic->pcie_current_link_speed), NULL, "%s", buf );
     551          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/max_link_speed", name ) );
     552          15 :   if( !read_cstr( path, buf, sizeof(buf) ) ) fd_cstr_printf( nic->pcie_max_link_speed, sizeof(nic->pcie_max_link_speed), NULL, "%s", buf );
     553             : 
     554          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/device", name ) );
     555          15 :   if( !read_ulong( path, &v ) ) nic->pci_device_id = (ushort)v;
     556          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/vendor", name ) );
     557          15 :   if( !read_ulong( path, &v ) ) nic->pci_vendor_id = (ushort)v;
     558          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/subsystem_vendor", name ) );
     559          15 :   if( !read_ulong( path, &v ) ) nic->pci_subsystem_vendor_id = (ushort)v;
     560          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/subsystem_device", name ) );
     561          15 :   if( !read_ulong( path, &v ) ) nic->pci_subsystem_device_id = (ushort)v;
     562             : 
     563          15 :   long l;
     564          15 :   nic->numa_node = ULONG_MAX;
     565          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/numa_node", name ) );
     566          15 :   if( !read_long( path, &l ) && l>=0L ) nic->numa_node = (ulong)l;
     567             : 
     568          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/mtu", name ) );
     569          15 :   read_ulong( path, &nic->mtu );
     570          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/speed", name ) );
     571          15 :   if( !read_long( path, &l ) && l>0L ) nic->link_speed_mbps = (ulong)l;
     572          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/carrier", name ) );
     573          15 :   nic->carrier = !read_ulong( path, &v ) && v==1UL;
     574          15 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/flags", name ) );
     575          15 :   if( !read_ulong( path, &v ) ) nic->promiscuous = !!( v&0x100UL ); /* IFF_PROMISC */
     576             : 
     577          15 :   struct ethtool_channels ch = { .cmd = ETHTOOL_GCHANNELS };
     578          15 :   if( !ethtool_ioc( fd, name, &ch ) ) {
     579          12 :     nic->channels_current = (ushort)fd_uint_min( ch.combined_count ? ch.combined_count : ch.rx_count, USHORT_MAX );
     580          12 :     nic->channels_max     = (ushort)fd_uint_min( ch.max_combined   ? ch.max_combined   : ch.max_rx,   USHORT_MAX );
     581          12 :   }
     582             : 
     583          15 :   struct ethtool_ringparam ring = { .cmd = ETHTOOL_GRINGPARAM };
     584          15 :   if( !ethtool_ioc( fd, name, &ring ) ) {
     585          12 :     nic->rx_ring_size = ring.rx_pending;
     586          12 :     nic->tx_ring_size = ring.tx_pending;
     587          12 :     nic->rx_ring_max  = ring.rx_max_pending;
     588          12 :     nic->tx_ring_max  = ring.tx_max_pending;
     589          12 :   }
     590             : 
     591             :   /* enabled kernel device features, under their kernel names */
     592          15 :   union { struct ethtool_sset_info hdr; uchar pad[ sizeof(struct ethtool_sset_info)+sizeof(uint) ]; } sset = {0};
     593          15 :   sset.hdr.cmd       = ETHTOOL_GSSET_INFO;
     594          15 :   sset.hdr.sset_mask = 1UL<<ETH_SS_FEATURES;
     595          15 :   uint feat_cnt = 0U;
     596          15 :   if( !ethtool_ioc( fd, name, &sset ) && sset.hdr.sset_mask ) feat_cnt = fd_uint_min( sset.hdr.data[ 0 ], 128U );
     597          15 :   if( feat_cnt ) {
     598          15 :     union { struct ethtool_gstrings hdr; uchar pad[ sizeof(struct ethtool_gstrings)+128UL*ETH_GSTRING_LEN ]; } strs = {0};
     599          15 :     strs.hdr.cmd        = ETHTOOL_GSTRINGS;
     600          15 :     strs.hdr.string_set = ETH_SS_FEATURES;
     601          15 :     strs.hdr.len        = feat_cnt;
     602          15 :     union { struct ethtool_gfeatures hdr; uchar pad[ sizeof(struct ethtool_gfeatures)+4UL*sizeof(struct ethtool_get_features_block) ]; } feats = {0};
     603          15 :     feats.hdr.cmd  = ETHTOOL_GFEATURES;
     604          15 :     feats.hdr.size = fd_uint_min( ( feat_cnt+31U )/32U, 4U );
     605          15 :     if( !ethtool_ioc( fd, name, &strs ) && !ethtool_ioc( fd, name, &feats ) ) {
     606         975 :       for( uint i=0U; i<feat_cnt && i/32U<feats.hdr.size && nic->offloads_cnt<64UL; i++ ) {
     607         960 :         if( !( feats.hdr.features[ i/32U ].active & (1U<<(i%32U)) ) ) continue;
     608         291 :         char fname[ ETH_GSTRING_LEN+1 ];
     609         291 :         fd_memcpy( fname, strs.hdr.data+i*ETH_GSTRING_LEN, ETH_GSTRING_LEN );
     610         291 :         fname[ ETH_GSTRING_LEN ] = '\0';
     611         291 :         if( !fname[ 0 ] ) continue;
     612         291 :         FD_TEST( fd_cstr_printf_check( nic->offloads[ nic->offloads_cnt++ ], sizeof(nic->offloads[ 0 ]), NULL, "%s", fname ) );
     613         291 :       }
     614          15 :     }
     615          15 :   }
     616             : 
     617          15 :   struct ethtool_rxnfc rxfh = { .cmd = ETHTOOL_GRXFH, .flow_type = UDP_V4_FLOW };
     618          15 :   if( !ethtool_ioc( fd, name, &rxfh ) )
     619           6 :     nic->rss_flow_hash_udp4 = ( rxfh.data&(RXH_L4_B_0_1|RXH_L4_B_2_3) )==(RXH_L4_B_0_1|RXH_L4_B_2_3);
     620             : 
     621          15 :   struct ethtool_coalesce coal = { .cmd = ETHTOOL_GCOALESCE };
     622          15 :   if( !ethtool_ioc( fd, name, &coal ) ) {
     623          12 :     nic->coalesce_rx_usecs    = coal.rx_coalesce_usecs;
     624          12 :     nic->coalesce_adaptive_rx = !!coal.use_adaptive_rx_coalesce;
     625          12 :   }
     626             : 
     627          15 :   struct ethtool_rxfh rssh = { .cmd = ETHTOOL_GRSSH };
     628          15 :   if( !ethtool_ioc( fd, name, &rssh ) ) {
     629          12 :     if(      rssh.hfunc&(1U<<0) ) FD_TEST( fd_cstr_printf_check( nic->rss_hash_func, sizeof(nic->rss_hash_func), NULL, "%s", "toeplitz" ) );
     630           0 :     else if( rssh.hfunc&(1U<<1) ) FD_TEST( fd_cstr_printf_check( nic->rss_hash_func, sizeof(nic->rss_hash_func), NULL, "%s", "xor" ) );
     631           0 :     else if( rssh.hfunc&(1U<<2) ) FD_TEST( fd_cstr_printf_check( nic->rss_hash_func, sizeof(nic->rss_hash_func), NULL, "%s", "crc32" ) );
     632          12 :   }
     633             : 
     634          15 :   struct ethtool_rxnfc cnt = { .cmd = ETHTOOL_GRXCLSRLCNT };
     635          15 :   if( !ethtool_ioc( fd, name, &cnt ) ) nic->ntuple_rule_count = cnt.rule_cnt;
     636          15 : }
     637             : 
     638             : static void
     639             : collect_net( fd_boot_report_t * r,
     640           3 :              char const *       iface ) {
     641           3 :   int fd = socket( AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0 );
     642             : 
     643           3 :   DIR * dir = opendir( "/sys/class/net" );
     644           3 :   if( FD_LIKELY( dir ) ) {
     645           3 :     struct dirent * ent;
     646          33 :     while( (ent = readdir( dir )) ) {
     647          30 :       if( ent->d_name[ 0 ]=='.' ) continue;
     648          24 :       char path[ PATH_MAX ];
     649          24 :       FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device", ent->d_name ) );
     650          24 :       int physical = 0==access( path, F_OK );
     651          24 :       FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/bonding", ent->d_name ) );
     652          24 :       int is_bond = 0==access( path, F_OK );
     653          24 :       if( !physical && !is_bond ) continue;
     654          15 :       if( fd>=0 ) collect_one_nic( r, fd, ent->d_name, is_bond );
     655          15 :     }
     656           3 :     closedir( dir );
     657           3 :   }
     658             : 
     659             :   /* bond membership of the bound interface, and its inventory index */
     660           3 :   r->bond_mode = 1; /* none */
     661           3 :   if( iface[ 0 ] ) {
     662           0 :     char path[ PATH_MAX ], buf[ 512 ];
     663           0 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/bonding/mode", iface ) );
     664           0 :     if( !read_cstr( path, buf, sizeof(buf) ) ) {
     665           0 :       if(      !strncmp( buf, "active-backup", 13UL ) ) r->bond_mode = 2;
     666           0 :       else if( !strncmp( buf, "802.3ad",        7UL ) ) r->bond_mode = 3;
     667           0 :       else if( !strncmp( buf, "balance-xor",   11UL ) ) r->bond_mode = 4;
     668           0 :       else                                              r->bond_mode = 5;
     669           0 :       FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/bonding/slaves", iface ) );
     670           0 :       if( !read_cstr( path, buf, sizeof(buf) ) ) {
     671           0 :         char * saveptr = NULL;
     672           0 :         for( char * slave = strtok_r( buf, " ", &saveptr ); slave && r->nic_bond_slaves_cnt<16UL; slave = strtok_r( NULL, " ", &saveptr ) ) {
     673           0 :           FD_TEST( fd_cstr_printf_check( r->nic_bond_slaves[ r->nic_bond_slaves_cnt ], 17UL, NULL, "%s", slave ) );
     674           0 :           r->nic_bond_slaves_cnt++;
     675           0 :         }
     676           0 :       }
     677           0 :     }
     678           0 :   }
     679           3 :   r->bound_nic_idx = 255;
     680          18 :   for( ulong i=0UL; i<r->nic_devices_cnt; i++ )
     681          15 :     if( !strcmp( r->nic_devices[ i ].name, iface ) ) { r->bound_nic_idx = (uchar)i; break; }
     682             : 
     683           3 :   ulong v;
     684           3 :   if( !read_ulong( "/proc/sys/net/core/bpf_jit_enable", &v ) ) r->net_bpf_jit_enable = (uchar)fd_ulong_min( v, UCHAR_MAX );
     685             : 
     686           3 :   if( fd>=0 ) close( fd );
     687           3 : }
     688             : 
     689             : /* platform: dmi identity, chassis, iommu, hypervisor */
     690             : 
     691             : static void
     692             : collect_platform( fd_boot_report_t * r,
     693           3 :                   char const *       iface ) {
     694           3 :   read_cstr( "/sys/class/dmi/id/sys_vendor",   r->dmi_sys_vendor,   sizeof(r->dmi_sys_vendor)   );
     695           3 :   read_cstr( "/sys/class/dmi/id/product_name", r->dmi_product_name, sizeof(r->dmi_product_name) );
     696           3 :   read_cstr( "/sys/class/dmi/id/bios_version", r->dmi_bios_version, sizeof(r->dmi_bios_version) );
     697           3 :   read_cstr( "/sys/class/dmi/id/bios_date",    r->dmi_bios_date,    sizeof(r->dmi_bios_date)    );
     698           3 :   read_cstr( "/sys/class/dmi/id/board_vendor", r->dmi_board_vendor, sizeof(r->dmi_board_vendor) );
     699           3 :   read_cstr( "/sys/class/dmi/id/board_name",   r->dmi_board_name,   sizeof(r->dmi_board_name)   );
     700             : 
     701           3 :   r->dmi_chassis_type = 1; /* unknown */
     702           3 :   ulong chassis;
     703           3 :   if( !read_ulong( "/sys/class/dmi/id/chassis_type", &chassis ) ) {
     704           3 :     switch( chassis ) { /* SMBIOS system enclosure types */
     705           0 :       case  3: case  4: case 13: case 15: r->dmi_chassis_type =  2; break; /* desktop */
     706           0 :       case  9:                            r->dmi_chassis_type =  3; break; /* laptop */
     707           0 :       case 10: case 14:                   r->dmi_chassis_type =  4; break; /* notebook */
     708           0 :       case  6: case  7:                   r->dmi_chassis_type =  5; break; /* tower */
     709           3 :       case 17: case 23:                   r->dmi_chassis_type =  6; break; /* rack_mount */
     710           0 :       case 28:                            r->dmi_chassis_type =  7; break; /* blade */
     711           0 :       case 29:                            r->dmi_chassis_type =  8; break; /* blade_enclosure */
     712           0 :       case 35:                            r->dmi_chassis_type =  9; break; /* mini_pc */
     713           0 :       case 36:                            r->dmi_chassis_type = 10; break; /* stick_pc */
     714           0 :       case 33: case 34:                   r->dmi_chassis_type = 11; break; /* embedded */
     715           0 :       case 24:                            r->dmi_chassis_type = 12; break; /* sealed_case_pc */
     716           0 :       case 31:                            r->dmi_chassis_type = 13; break; /* convertible */
     717           0 :       case 32:                            r->dmi_chassis_type = 14; break; /* detachable */
     718           0 :       default:                                                     break;
     719           3 :     }
     720           3 :   }
     721             : 
     722             :   /* iommu: no groups means off; the bound NIC's group default domain
     723             :      type distinguishes passthrough from translated */
     724           3 :   r->iommu_mode = 1; /* off */
     725           3 :   DIR * groups = opendir( "/sys/kernel/iommu_groups" );
     726           3 :   if( groups ) {
     727           3 :     struct dirent * ent;
     728           3 :     int have_group = 0;
     729           9 :     while( (ent = readdir( groups )) ) {
     730           9 :       if( ent->d_name[ 0 ]!='.' ) { have_group = 1; break; }
     731           9 :     }
     732           3 :     closedir( groups );
     733           3 :     if( have_group ) {
     734           3 :       r->iommu_mode = 4; /* unknown */
     735           3 :       char path[ PATH_MAX ], buf[ 64 ];
     736             :       /* a bonded interface is virtual and has no iommu group; fall
     737             :          back to the first member */
     738           9 :       for( int attempt=0; attempt<2 && r->iommu_mode==4; attempt++ ) {
     739           6 :         char const * nic = attempt ? ( r->nic_bond_slaves_cnt ? r->nic_bond_slaves[ 0 ] : "" ) : iface;
     740           6 :         if( !nic[ 0 ] ) continue;
     741           0 :         FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/net/%s/device/iommu_group/type", nic ) );
     742           0 :         if( read_cstr( path, buf, sizeof(buf) ) ) continue;
     743           0 :         if(      !strcmp ( buf, "identity" )    ) r->iommu_mode = 2; /* passthrough */
     744           0 :         else if( !strncmp( buf, "DMA", 3UL )    ) r->iommu_mode = 3; /* translated */
     745           0 :       }
     746           3 :     }
     747           3 :   }
     748             : 
     749           3 :   r->hypervisor_vendor = 1; /* none */
     750           3 : # if defined(__x86_64__)
     751           3 :   uint eax, ebx, ecx, edx;
     752           3 :   if( __get_cpuid( 1U, &eax, &ebx, &ecx, &edx ) && ( ecx&(1U<<31) ) ) {
     753           0 :     r->hypervisor_vendor = 13; /* unknown */
     754           0 :     __cpuid( 0x40000000U, eax, ebx, ecx, edx ); /* __get_cpuid rejects hypervisor leaves */
     755           0 :     char sig[ 13 ];
     756           0 :     fd_memcpy( sig,   &ebx, 4UL );
     757           0 :     fd_memcpy( sig+4, &ecx, 4UL );
     758           0 :     fd_memcpy( sig+8, &edx, 4UL );
     759           0 :     sig[ 12 ] = '\0';
     760           0 :     if( !strncmp( sig, "KVMKVMKVM", 9UL ) ) {
     761           0 :       r->hypervisor_vendor = 2; /* kvm */
     762           0 :       if(      strcasestr( r->dmi_sys_vendor,   "amazon" ) ) r->hypervisor_vendor = 7; /* amazon_nitro */
     763           0 :       else if( strcasestr( r->dmi_product_name, "google" ) ) r->hypervisor_vendor = 8; /* kvm_gce */
     764           0 :     }
     765           0 :     else if( !strncmp( sig, "TCGTCGTCG",    9UL ) ) r->hypervisor_vendor =  3; /* qemu */
     766           0 :     else if( !strncmp( sig, "VMwareVMware", 12UL ) ) r->hypervisor_vendor =  4; /* vmware */
     767           0 :     else if( !strncmp( sig, "Microsoft Hv", 12UL ) ) r->hypervisor_vendor =  5; /* microsoft_hyperv */
     768           0 :     else if( !strncmp( sig, "XenVMMXenVMM", 12UL ) ) r->hypervisor_vendor =  6; /* xen */
     769           0 :     else if( !strncmp( sig, "bhyve bhyve",  11UL ) ) r->hypervisor_vendor =  9; /* bhyve */
     770           0 :     else if( strstr( sig, "prl hyperv" )           ) r->hypervisor_vendor = 10; /* parallels */
     771           0 :     else if( !strncmp( sig, "VBoxVBoxVBox", 12UL ) ) r->hypervisor_vendor = 11; /* virtualbox */
     772           0 :     if( r->hypervisor_vendor==13 && strcasestr( r->dmi_product_name, "bochs" ) ) r->hypervisor_vendor = 12;
     773           0 :   }
     774           3 : # endif /* defined(__x86_64__) */
     775             : 
     776             :   /* virtualized hosts override the chassis form factor: a recognized
     777             :      cloud platform reports cloud, any other hypervisor virtual (bare
     778             :      metal providers leave no identifier on the machine) */
     779           3 :   if( r->hypervisor_vendor!=1 ) {
     780           0 :     static char const * const cloud_tbl[] = { "amazon", "google", "microsoft", "alibaba", "digitalocean", "vultr", "ovh", "hetzner", "oracle", "equinix", "latitude", "scaleway" };
     781           0 :     int cloud = 0;
     782           0 :     for( ulong i=0UL; i<sizeof(cloud_tbl)/sizeof(cloud_tbl[0]); i++ ) {
     783           0 :       if( strcasestr( r->dmi_sys_vendor,   cloud_tbl[ i ] ) ||
     784           0 :           strcasestr( r->dmi_product_name, cloud_tbl[ i ] ) ||
     785           0 :           strcasestr( r->dmi_board_vendor, cloud_tbl[ i ] ) ) { cloud = 1; break; }
     786           0 :     }
     787           0 :     r->dmi_chassis_type = cloud ? 16 : 15; /* cloud : virtual */
     788           0 :   }
     789           3 : }
     790             : 
     791             : static void
     792           3 : collect_container( fd_boot_report_t * r ) {
     793           3 :   char cgroup[ 4096 ] = {0};
     794           3 :   read_file( "/proc/self/cgroup", cgroup, sizeof(cgroup), NULL );
     795             : 
     796           3 :   r->cgroup_swap_limit_bytes = ULONG_MAX;
     797             : 
     798           3 :   r->container_type = 1; /* none */
     799           3 :   char buf[ 128 ];
     800           3 :   if( !read_cstr( "/run/systemd/container", buf, sizeof(buf) ) ) {
     801           0 :     if(      !strcmp( buf, "docker"         ) ) r->container_type = 2;
     802           0 :     else if( !strcmp( buf, "podman"         ) ) r->container_type = 3;
     803           0 :     else if( !strcmp( buf, "lxc"            ) ) r->container_type = 6;
     804           0 :     else if( !strcmp( buf, "systemd-nspawn" ) ) r->container_type = 7;
     805           0 :     else                                        r->container_type = 8; /* unknown runtime */
     806           0 :   }
     807           3 :   else if( 0==access( "/.dockerenv"       , F_OK ) ) r->container_type = 2;
     808           3 :   else if( 0==access( "/run/.containerenv", F_OK ) ) r->container_type = 3;
     809           3 :   else if( strstr( cgroup, "cri-containerd" ) )  r->container_type = 4;
     810           3 :   else if( strstr( cgroup, "crio" ) )            r->container_type = 5;
     811           3 :   else if( strstr( cgroup, "docker" ) )          r->container_type = 2;
     812           3 :   else if( strstr( cgroup, "libpod" ) )          r->container_type = 3;
     813           3 :   else if( strstr( cgroup, "lxc" ) )             r->container_type = 6;
     814             : 
     815           3 :   r->kubernetes_pod = !!getenv( "KUBERNETES_SERVICE_HOST" ) || !!strstr( cgroup, "kubepods" );
     816             : 
     817             :   /* cgroup v2 limits along our own cgroup path */
     818           3 :   char const * v2 = strstr( cgroup, "0::" );
     819           3 :   if( v2 ) {
     820           3 :     char cgpath[ PATH_MAX ];
     821           3 :     FD_TEST( fd_cstr_printf_check( cgpath, sizeof(cgpath), NULL, "%s", v2+3 ) );
     822           3 :     char * nl = strchr( cgpath, '\n' );
     823           3 :     if( nl ) *nl = '\0';
     824             : 
     825           3 :     char path[ PATH_MAX+32 ], val[ 64 ];
     826           3 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup%s/memory.max", cgpath ) );
     827           3 :     if( !read_cstr( path, val, sizeof(val) ) && strcmp( val, "max" ) ) r->cgroup_memory_limit_bytes = strtoul( val, NULL, 10 );
     828           3 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup%s/memory.high", cgpath ) );
     829           3 :     if( !read_cstr( path, val, sizeof(val) ) && strcmp( val, "max" ) ) r->cgroup_memory_high_bytes = strtoul( val, NULL, 10 );
     830           3 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup%s/memory.swap.max", cgpath ) );
     831           3 :     if( !read_cstr( path, val, sizeof(val) ) ) r->cgroup_swap_limit_bytes = strcmp( val, "max" ) ? strtoul( val, NULL, 10 ) : ULONG_MAX;
     832           3 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup%s/cpu.max", cgpath ) );
     833           3 :     if( !read_cstr( path, val, sizeof(val) ) ) {
     834           0 :       ulong quota, period;
     835           0 :       if( 2==sscanf( val, "%lu %lu", &quota, &period ) && period ) r->cgroup_cpu_quota_millicores = (uint)( quota*1000UL/period );
     836           0 :     }
     837           3 :   }
     838             : 
     839             :   /* cgroup v1: per-controller hierarchies, used by v1-only and hybrid
     840             :      hosts; v2 wins where it already produced a value.  v1 encodes
     841             :      unlimited as a page-rounded LLONG_MAX and swap only as the
     842             :      combined memory+swap limit */
     843           6 :   for( char const * line=cgroup; *line; ) {
     844           3 :     char const * nl   = strchr( line, '\n' );
     845           3 :     ulong        llen = nl ? (ulong)( nl-line ) : strlen( line );
     846           3 :     char const * c1 = memchr( line, ':', llen );
     847           3 :     char const * c2 = c1 ? memchr( c1+1, ':', (ulong)( line+llen-(c1+1) ) ) : NULL;
     848           3 :     if( c2 ) {
     849           3 :       char ctl[ 64 ];
     850           3 :       fd_cstr_printf( ctl, fd_ulong_min( (ulong)( c2-(c1+1) )+1UL, sizeof(ctl) ), NULL, "%s", c1+1 );
     851           3 :       char cgpath[ PATH_MAX ];
     852           3 :       fd_cstr_printf( cgpath, fd_ulong_min( (ulong)( line+llen-(c2+1) )+1UL, sizeof(cgpath) ), NULL, "%s", c2+1 );
     853           3 :       int mem = 0, cpu = 0;
     854           3 :       char * sp = NULL;
     855           3 :       for( char * tok = strtok_r( ctl, ",", &sp ); tok; tok = strtok_r( NULL, ",", &sp ) ) {
     856           0 :         mem |= !strcmp( tok, "memory" );
     857           0 :         cpu |= !strcmp( tok, "cpu" );
     858           0 :       }
     859           3 :       char path[ PATH_MAX+64 ];
     860           3 :       ulong l;
     861           3 :       if( mem && !r->cgroup_memory_limit_bytes ) {
     862           0 :         FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup/memory%s/memory.limit_in_bytes", cgpath ) );
     863           0 :         if( !read_ulong( path, &l ) && l<(1UL<<60) ) r->cgroup_memory_limit_bytes = l;
     864           0 :         FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup/memory%s/memory.memsw.limit_in_bytes", cgpath ) );
     865           0 :         if( !read_ulong( path, &l ) && l<(1UL<<60) && r->cgroup_memory_limit_bytes ) r->cgroup_swap_limit_bytes = l-r->cgroup_memory_limit_bytes;
     866           0 :       }
     867           3 :       if( cpu && !r->cgroup_cpu_quota_millicores ) {
     868           0 :         long quota;
     869           0 :         FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup/cpu%s/cpu.cfs_quota_us", cgpath ) );
     870           0 :         if( !read_long( path, &quota ) && quota>0L ) {
     871           0 :           FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup/cpu%s/cpu.cfs_period_us", cgpath ) );
     872           0 :           if( !read_ulong( path, &l ) && l ) r->cgroup_cpu_quota_millicores = (uint)( (ulong)quota*1000UL/l );
     873           0 :         }
     874           0 :       }
     875           3 :     }
     876           3 :     if( !nl ) break;
     877           3 :     line = nl+1;
     878           3 :   }
     879           3 : }
     880             : 
     881             : /* storage: block device inventory, nvme health, mdraid, and the
     882             :    filesystems backing the validator's directories */
     883             : 
     884             : static void
     885           3 : collect_block_devices( fd_boot_report_t * r ) {
     886           3 :   DIR * dir = opendir( "/sys/block" );
     887           3 :   if( FD_UNLIKELY( !dir ) ) return;
     888           3 :   struct dirent * ent;
     889          42 :   while( (ent = readdir( dir )) && r->block_devices_cnt<24UL ) {
     890          39 :     char const * n = ent->d_name;
     891          39 :     if( n[ 0 ]=='.' ) continue;
     892          33 :     if( !strncmp( n, "loop", 4UL ) || !strncmp( n, "ram", 3UL ) || !strncmp( n, "zram", 4UL ) ||
     893          33 :         !strncmp( n, "sr", 2UL )   || !strncmp( n, "fd", 2UL )  || !strncmp( n, "nbd", 3UL )  ||
     894          33 :         !strncmp( n, "dm-", 3UL ) ) continue;
     895             : 
     896          24 :     fd_boot_report_block_device_t * d = &r->block_devices[ r->block_devices_cnt++ ];
     897          24 :     fd_memset( d, 0, sizeof(*d) );
     898          24 :     fd_cstr_printf( d->name, sizeof(d->name), NULL, "%s", n );
     899             : 
     900          24 :     char path[ PATH_MAX ], buf[ 256 ];
     901          24 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/device/model", n ) );
     902          24 :     if( !read_cstr( path, buf, sizeof(buf) ) ) fd_cstr_printf( d->model, sizeof(d->model), NULL, "%s", buf );
     903             : 
     904          24 :     ulong v;
     905          24 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/size", n ) );
     906          24 :     if( !read_ulong( path, &v ) ) d->capacity_bytes = v*512UL;
     907          24 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/queue/rotational", n ) );
     908          24 :     if( !read_ulong( path, &v ) ) d->rotational = !!v;
     909             : 
     910          24 :     if(      !strncmp( n, "nvme",   4UL ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "nvme" ) );
     911          12 :     else if( !strncmp( n, "vd",     2UL ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "virtio" ) );
     912          12 :     else if( !strncmp( n, "xvd",    3UL ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "xen" ) );
     913          12 :     else if( !strncmp( n, "mmcblk", 6UL ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "mmc" ) );
     914          12 :     else if( !strncmp( n, "md",     2UL ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "md" ) );
     915           0 :     else {
     916           0 :       char target[ PATH_MAX ];
     917           0 :       FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s", n ) );
     918           0 :       long tn = readlink( path, target, sizeof(target)-1UL );
     919           0 :       if( tn>0L ) target[ tn ] = '\0'; else target[ 0 ] = '\0';
     920           0 :       if(      strstr( target, "/usb" ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "usb" ) );
     921           0 :       else if( strstr( target, "/ata" ) ) FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "sata" ) );
     922           0 :       else                                FD_TEST( fd_cstr_printf_check( d->transport, sizeof(d->transport), NULL, "%s", "scsi" ) );
     923           0 :     }
     924             : 
     925          24 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/queue/scheduler", n ) );
     926          24 :     if( !read_cstr( path, buf, sizeof(buf) ) ) {
     927          12 :       char * lb = strchr( buf, '[' );
     928          12 :       char * rb = lb ? strchr( lb, ']' ) : NULL;
     929          12 :       if( lb && rb ) { *rb = '\0'; fd_cstr_printf( d->scheduler, sizeof(d->scheduler), NULL, "%s", lb+1 ); }
     930           0 :       else           fd_cstr_printf( d->scheduler, sizeof(d->scheduler), NULL, "%s", buf );
     931          12 :     }
     932          24 :   }
     933           3 :   closedir( dir );
     934           3 : }
     935             : 
     936             : /* nvme get log page: local definitions to avoid kernel header drift */
     937             : 
     938             : struct nvme_admin_cmd_ioc {
     939             :   uchar  opcode; uchar flags; ushort rsvd1;
     940             :   uint   nsid;   uint cdw2; uint cdw3;
     941             :   ulong  metadata; ulong addr;
     942             :   uint   metadata_len; uint data_len;
     943             :   uint   cdw10; uint cdw11; uint cdw12; uint cdw13; uint cdw14; uint cdw15;
     944             :   uint   timeout_ms; uint result;
     945             : };
     946             : 
     947           0 : #define NVME_IOCTL_ADMIN_CMD_ _IOWR( 'N', 0x41, struct nvme_admin_cmd_ioc )
     948             : 
     949             : static void
     950           3 : collect_nvme( fd_boot_report_t * r ) {
     951          27 :   for( ulong i=0UL; i<r->block_devices_cnt && r->nvme_devices_cnt<16UL; i++ ) {
     952          24 :     char const * n = r->block_devices[ i ].name;
     953          24 :     uint ctrl_num, ns_num;
     954          24 :     if( 2!=sscanf( n, "nvme%un%u", &ctrl_num, &ns_num ) ) continue;
     955             : 
     956          12 :     fd_boot_report_nvme_t * d = &r->nvme_devices[ r->nvme_devices_cnt++ ];
     957          12 :     fd_memset( d, 0, sizeof(*d) );
     958          12 :     d->device_idx           = (uchar)i;
     959          12 :     d->namespace_size_bytes = r->block_devices[ i ].capacity_bytes;
     960          12 :     d->numa_node            = 255;
     961             : 
     962          12 :     char path[ PATH_MAX ], buf[ 256 ];
     963          12 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/device/firmware_rev", n ) );
     964          12 :     if( !read_cstr( path, buf, sizeof(buf) ) ) fd_cstr_printf( d->firmware_rev, sizeof(d->firmware_rev), NULL, "%s", buf );
     965          12 :     long l;
     966          12 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/device/device/numa_node", n ) );
     967          12 :     if( !read_long( path, &l ) && l>=0L && l<255L ) d->numa_node = (uchar)l;
     968             : 
     969          12 :     char ctrl[ 64 ];
     970          12 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/device", n ) );
     971          12 :     readlink_base( path, ctrl, sizeof(ctrl) );
     972          12 :     if( !ctrl[ 0 ] ) FD_TEST( fd_cstr_printf_check( ctrl, sizeof(ctrl), NULL, "nvme%u", ctrl_num ) );
     973             : 
     974          12 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/dev/%s", ctrl ) );
     975          12 :     int fd = open( path, O_RDONLY|O_CLOEXEC );
     976          12 :     if( fd<0 ) continue;
     977           0 :     uchar log[ 512 ] __attribute__((aligned(8))) = {0};
     978           0 :     struct nvme_admin_cmd_ioc cmd = {
     979           0 :       .opcode   = 0x02, /* get log page */
     980           0 :       .nsid     = 0xFFFFFFFFU,
     981           0 :       .addr     = (ulong)log,
     982           0 :       .data_len = 512U,
     983           0 :       .cdw10    = 0x02U | ( ( 512U/4U-1U )<<16 ), /* smart / health, numdl */
     984           0 :     };
     985           0 :     if( 0==ioctl( fd, NVME_IOCTL_ADMIN_CMD_, &cmd ) ) {
     986           0 :       d->critical_warning    = log[ 0 ];
     987           0 :       d->available_spare_pct = log[ 3 ];
     988           0 :       d->percentage_used     = log[ 5 ];
     989           0 :       fd_memcpy( &d->media_errors, log+160, 8UL ); /* low 64 bits of le128 */
     990           0 :     }
     991           0 :     close( fd );
     992           0 :   }
     993           3 : }
     994             : 
     995             : static void
     996           3 : collect_mdraid( fd_boot_report_t * r ) {
     997          27 :   for( ulong i=0UL; i<r->block_devices_cnt && r->mdraid_arrays_cnt<16UL; i++ ) {
     998          24 :     char const * n = r->block_devices[ i ].name;
     999          24 :     if( strncmp( n, "md", 2UL ) ) continue;
    1000          12 :     char path[ PATH_MAX ], buf[ 64 ];
    1001          12 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/md/level", n ) );
    1002          12 :     if( read_cstr( path, buf, sizeof(buf) ) ) continue;
    1003             : 
    1004          12 :     fd_boot_report_mdraid_t * d = &r->mdraid_arrays[ r->mdraid_arrays_cnt++ ];
    1005          12 :     fd_memset( d, 0, sizeof(*d) );
    1006          12 :     fd_cstr_printf( d->name, sizeof(d->name), NULL, "%s", n );
    1007          12 :     fd_cstr_printf( d->level, sizeof(d->level), NULL, "%s", buf );
    1008          12 :     ulong v;
    1009          12 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/md/raid_disks", n ) );
    1010          12 :     if( !read_ulong( path, &v ) ) d->device_count = (uchar)fd_ulong_min( v, UCHAR_MAX );
    1011          12 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/block/%s/md/degraded", n ) );
    1012          12 :     if( !read_ulong( path, &v ) ) d->degraded = !!v;
    1013          12 :   }
    1014           3 : }
    1015             : 
    1016             : /* filesystems: resolve each configured directory to its mount via
    1017             :    longest-prefix match over mountinfo, dedupe mounts into the
    1018             :    filesystems array, and link each mount back to its disk */
    1019             : 
    1020             : struct mount_ent {
    1021             :   char  mount_point[ PATH_MAX ];
    1022             :   char  fs_type[ 17 ];
    1023             :   char  options[ 257 ];
    1024             :   uint  dev_major;
    1025             :   uint  dev_minor;
    1026             : };
    1027             : 
    1028             : static ulong
    1029             : parse_mountinfo( struct mount_ent * mounts,
    1030             :                  ulong              max,
    1031           3 :                  int *              truncated ) {
    1032           3 :   static char buf[ 1048576 ];
    1033           3 :   ulong len;
    1034           3 :   *truncated = 0;
    1035           3 :   if( read_file( "/proc/self/mountinfo", buf, sizeof(buf), &len ) ) return 0UL;
    1036           3 :   if( len==sizeof(buf)-1UL ) *truncated = 1;
    1037           3 :   ulong cnt = 0UL;
    1038           3 :   char * saveptr = NULL;
    1039           3 :   char * line = strtok_r( buf, "\n", &saveptr );
    1040          96 :   for( ; line && cnt<max; line = strtok_r( NULL, "\n", &saveptr ) ) {
    1041          93 :     char * f[ 32 ]; ulong nf = 0UL;
    1042          93 :     char * sp = NULL;
    1043        1116 :     for( char * tok = strtok_r( line, " ", &sp ); tok && nf<32UL; tok = strtok_r( NULL, " ", &sp ) ) f[ nf++ ] = tok;
    1044          93 :     if( nf<10UL ) continue;
    1045          93 :     ulong dash = 6UL;
    1046         186 :     while( dash<nf && strcmp( f[ dash ], "-" ) ) dash++;
    1047          93 :     if( dash+2UL>=nf ) continue;
    1048             : 
    1049          93 :     struct mount_ent * m = &mounts[ cnt++ ];
    1050          93 :     fd_memset( m, 0, sizeof(*m) );
    1051          93 :     sscanf( f[ 2 ], "%u:%u", &m->dev_major, &m->dev_minor );
    1052          93 :     fd_cstr_printf( m->mount_point, sizeof(m->mount_point), NULL, "%s", f[ 4 ] );
    1053             :     /* mountinfo octal-escapes space, tab, newline and backslash */
    1054          93 :     char * w = m->mount_point;
    1055        1596 :     for( char const * q=m->mount_point; *q; q++ ) {
    1056        1503 :       if( q[0]=='\\' && q[1]>='0' && q[1]<='3' && q[2]>='0' && q[2]<='7' && q[3]>='0' && q[3]<='7' ) {
    1057           0 :         *w++ = (char)( ((q[1]-'0')<<6) | ((q[2]-'0')<<3) | (q[3]-'0') );
    1058           0 :         q += 3;
    1059        1503 :       } else *w++ = *q;
    1060        1503 :     }
    1061          93 :     *w = '\0';
    1062          93 :     fd_cstr_printf( m->fs_type,     sizeof(m->fs_type), NULL, "%s",     f[ dash+1UL ] );
    1063             :     /* per-mount flags then superblock options; superblock option
    1064             :        values can embed paths, addresses and usernames (overlayfs
    1065             :        lowerdir, nfs addr, cifs username), so values are kept only for
    1066             :        allowlisted keys */
    1067          93 :     static char const * const opt_val_keys[] = {
    1068          93 :       "allocsize", "barrier", "commit", "compress", "compress-force", "data", "discard", "errors", "huge",
    1069          93 :       "logbsize", "logbufs", "mode", "nr_inodes", "size", "space_cache", "stripe", "subvolid", "sunit", "swidth",
    1070          93 :     };
    1071          93 :     char * out = m->options;
    1072          93 :     ulong  rem = sizeof(m->options);
    1073         279 :     for( ulong s=0UL; s<2UL && nf>dash+3UL*s; s++ ) {
    1074         186 :       char * osp = NULL;
    1075         879 :       for( char * opt = strtok_r( s ? f[ dash+3UL ] : f[ 5 ], ",", &osp ); opt; opt = strtok_r( NULL, ",", &osp ) ) {
    1076         693 :         char const * eq = strchr( opt, '=' );
    1077         693 :         int redact = !!eq;
    1078        3144 :         for( ulong k=0UL; eq && k<sizeof(opt_val_keys)/sizeof(opt_val_keys[0]); k++ ) {
    1079        2451 :           if( (ulong)( eq-opt )==strlen( opt_val_keys[ k ] ) && !strncmp( opt, opt_val_keys[ k ], (ulong)( eq-opt ) ) ) redact = 0;
    1080        2451 :         }
    1081         693 :         ulong n;
    1082         693 :         int fit = redact ? fd_cstr_printf_check( out, rem, &n, "%s%.*s=[redacted]", out==m->options ? "" : ",", (int)( eq-opt ), opt )
    1083         693 :                          : fd_cstr_printf_check( out, rem, &n, "%s%s",              out==m->options ? "" : ",", opt );
    1084         693 :         if( !fit ) { *out = '\0'; break; } /* capped at the schema length */
    1085         693 :         out += n; rem -= n;
    1086         693 :       }
    1087         186 :     }
    1088          93 :   }
    1089           3 :   if( line ) *truncated = 1; /* stopped at the entry cap */
    1090           3 :   return cnt;
    1091           3 : }
    1092             : 
    1093             : static long
    1094             : mount_for_path( char const *             path,
    1095             :                 struct mount_ent const * mounts,
    1096           9 :                 ulong                    mount_cnt ) {
    1097           9 :   long  best     = -1L;
    1098           9 :   ulong best_len = 0UL;
    1099         288 :   for( ulong i=0UL; i<mount_cnt; i++ ) {
    1100         279 :     char const * mp = mounts[ i ].mount_point;
    1101         279 :     ulong len = strlen( mp );
    1102         279 :     if( strncmp( path, mp, len ) ) continue;
    1103           9 :     if( len>1UL && path[ len ]!='\0' && path[ len ]!='/' ) continue;
    1104           9 :     if( len>=best_len ) { best = (long)i; best_len = len; }
    1105           9 :   }
    1106           9 :   return best;
    1107           9 : }
    1108             : 
    1109             : /* parent_disk resolves a block device name to its whole-disk name,
    1110             :    stripping a partition suffix via the sysfs hierarchy */
    1111             : 
    1112             : static void
    1113             : parent_disk( char const * dev,
    1114             :              char *       out,
    1115           9 :              ulong        out_sz ) {
    1116           9 :   char path[ PATH_MAX+32 ], resolved[ PATH_MAX ];
    1117           9 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/block/%s", dev ) );
    1118           9 :   if( !realpath( path, resolved ) ) { fd_cstr_printf( out, out_sz, NULL, "%s", dev ); return; }
    1119           9 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/partition", resolved ) );
    1120           9 :   if( 0==access( path, F_OK ) ) {
    1121           0 :     char * base = strrchr( resolved, '/' );
    1122           0 :     if( base ) *base = '\0'; /* partition -> parent disk */
    1123           0 :   }
    1124           9 :   char const * name = strrchr( resolved, '/' );
    1125           9 :   fd_cstr_printf( out, out_sz, NULL, "%s", name ? name+1 : resolved );
    1126           9 : }
    1127             : 
    1128             : static uchar
    1129             : device_idx_for_mount( fd_boot_report_t const * r,
    1130           3 :                       struct mount_ent const * m ) {
    1131           3 :   char path[ PATH_MAX+32 ], resolved[ PATH_MAX ], name[ 72 ];
    1132           3 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/dev/block/%u:%u", m->dev_major, m->dev_minor ) );
    1133           3 :   if( !realpath( path, resolved ) ) return 255;
    1134           3 :   char const * base = strrchr( resolved, '/' );
    1135           3 :   parent_disk( base ? base+1 : resolved, name, sizeof(name) );
    1136             : 
    1137             :   /* virtual devices not in the inventory (lvm volumes, partitioned
    1138             :      raid) descend through their first slave until a real device */
    1139           9 :   for( int depth=0; depth<4; depth++ ) {
    1140          63 :     for( ulong i=0UL; i<r->block_devices_cnt; i++ )
    1141          57 :       if( !strcmp( r->block_devices[ i ].name, name ) ) return (uchar)i;
    1142             : 
    1143           6 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/class/block/%s/slaves", name ) );
    1144           6 :     DIR * dir = opendir( path );
    1145           6 :     if( !dir ) return 255;
    1146           6 :     char slave[ 72 ] = {0};
    1147           6 :     struct dirent * ent;
    1148          18 :     while( (ent = readdir( dir )) ) if( ent->d_name[ 0 ]!='.' ) { fd_cstr_printf( slave, sizeof(slave), NULL, "%s", ent->d_name ); break; }
    1149           6 :     closedir( dir );
    1150           6 :     if( !slave[ 0 ] ) return 255;
    1151           6 :     parent_disk( slave, name, sizeof(name) );
    1152           6 :   }
    1153           0 :   return 255;
    1154           3 : }
    1155             : 
    1156             : static long fs_mount_idx[ 8 ]; /* mountinfo index behind each filesystems entry */
    1157             : 
    1158             : static uchar
    1159             : fs_idx_for_path( fd_boot_report_t *       r,
    1160             :                  char const *             path,
    1161             :                  struct mount_ent const * mounts,
    1162          15 :                  ulong                    mount_cnt ) {
    1163          15 :   if( !path[ 0 ] ) return 255;
    1164             :   /* canonicalize; a path not created yet attributes its deepest
    1165             :      existing ancestor, the same mount once it is */
    1166           9 :   char real[ PATH_MAX ], trunc[ PATH_MAX ];
    1167           9 :   FD_TEST( fd_cstr_printf_check( trunc, sizeof(trunc), NULL, "%s", path ) );
    1168           9 :   for(;;) {
    1169           9 :     if( realpath( trunc, real ) ) { path = real; break; }
    1170           0 :     char * slash = strrchr( trunc, '/' );
    1171           0 :     if( !slash ) break; /* relative and missing: unresolvable */
    1172           0 :     if( slash==trunc ) { if( trunc[ 1 ]=='\0' ) break; trunc[ 1 ] = '\0'; }
    1173           0 :     else *slash = '\0';
    1174           0 :   }
    1175           9 :   long mi = mount_for_path( path, mounts, mount_cnt );
    1176           9 :   if( mi<0L ) return 255;
    1177           9 :   struct mount_ent const * m = &mounts[ mi ];
    1178             : 
    1179           9 :   for( ulong i=0UL; i<r->filesystems_cnt; i++ ) if( fs_mount_idx[ i ]==mi ) return (uchar)i;
    1180           3 :   if( r->filesystems_cnt>=8UL ) return 255;
    1181           3 :   fs_mount_idx[ r->filesystems_cnt ] = mi;
    1182             : 
    1183           3 :   fd_boot_report_fs_t * fs = &r->filesystems[ r->filesystems_cnt ];
    1184           3 :   fd_memset( fs, 0, sizeof(*fs) );
    1185           3 :   fs->device_idx = device_idx_for_mount( r, m );
    1186           3 :   FD_TEST( fd_cstr_printf_check( fs->fs_type,       sizeof(fs->fs_type), NULL, "%s",       m->fs_type ) );
    1187           3 :   FD_TEST( fd_cstr_printf_check( fs->mount_options, sizeof(fs->mount_options), NULL, "%s", m->options ) );
    1188           3 :   struct statvfs sv;
    1189           3 :   if( 0==statvfs( m->mount_point, &sv ) ) {
    1190           3 :     fs->capacity_bytes = sv.f_blocks*sv.f_frsize;
    1191           3 :     fs->free_bytes     = sv.f_bavail*sv.f_frsize;
    1192           3 :   }
    1193           3 :   return (uchar)r->filesystems_cnt++;
    1194           3 : }
    1195             : 
    1196             : static void
    1197             : collect_filesystems( fd_boot_report_t *     r,
    1198           3 :                      fd_topo_tile_t const * tile ) {
    1199           3 :   static struct mount_ent mounts[ 512 ];
    1200           3 :   int truncated;
    1201           3 :   ulong mount_cnt = parse_mountinfo( mounts, 512UL, &truncated );
    1202             :   /* a mount past a truncated table could be the longest prefix; report
    1203             :      unresolved rather than a wrong ancestor */
    1204           3 :   if( FD_UNLIKELY( truncated ) ) mount_cnt = 0UL;
    1205             : 
    1206           3 :   r->accounts_fs_idx  = fs_idx_for_path( r, tile->event.accounts_path,  mounts, mount_cnt );
    1207           3 :   r->snapshots_fs_idx = fs_idx_for_path( r, tile->event.snapshots_path, mounts, mount_cnt );
    1208           3 :   r->log_fs_idx       = fs_idx_for_path( r, tile->event.log_path,       mounts, mount_cnt );
    1209           3 :   r->shredb_fs_idx    = fs_idx_for_path( r, tile->event.shredb_path,    mounts, mount_cnt );
    1210           3 :   r->guidb_fs_idx     = fs_idx_for_path( r, tile->event.guidb_path,     mounts, mount_cnt );
    1211           3 : }
    1212             : 
    1213             : void
    1214             : fd_boot_report_collect( fd_boot_report_t *     r,
    1215             :                         fd_topo_t const *      topo,
    1216           3 :                         fd_topo_tile_t const * tile ) {
    1217           3 :   fd_memset( r, 0, sizeof(*r) );
    1218             : 
    1219             :   /* rendered by the app during topology construction */
    1220           3 :   if( FD_UNLIKELY( !topo->resolved_config_json_len ) )
    1221           0 :     FD_LOG_ERR(( "boot report config documents were not rendered into the topology" ));
    1222           3 :   FD_TEST( topo->resolved_config_json_len<sizeof(r->resolved_config_json) );
    1223           3 :   FD_TEST( topo->user_config_json_len    <sizeof(r->user_config_json)     );
    1224           3 :   fd_memcpy( r->resolved_config_json, topo->resolved_config_json, topo->resolved_config_json_len );
    1225           3 :   r->resolved_config_json_len = topo->resolved_config_json_len;
    1226           3 :   fd_memcpy( r->user_config_json, topo->user_config_json, topo->user_config_json_len );
    1227           3 :   r->user_config_json_len = topo->user_config_json_len;
    1228             : 
    1229           3 :   collect_os( r );
    1230           3 :   collect_cpu( r );
    1231           3 :   collect_mem( r );
    1232           3 :   collect_dimms( r );
    1233           3 :   collect_net( r, tile->event.net_interface );
    1234           3 :   collect_platform( r, tile->event.net_interface );
    1235           3 :   collect_container( r );
    1236           3 :   collect_block_devices( r );
    1237           3 :   collect_nvme( r );
    1238           3 :   collect_mdraid( r );
    1239           3 :   collect_filesystems( r, tile );
    1240             :   /* stamped at command start in the parent; the tile clone comes later */
    1241           3 :   r->process_start_time_nanos = (ulong)tile->event.boot_timestamp_nanos;
    1242           3 :   r->feature_set_id = FD_FEATURE_SET_ID;
    1243           3 : }
    1244             : 
    1245             : /* topology json: tiles, links and workspaces from the live topo */
    1246             : 
    1247             : static void
    1248             : render_topology_json( fd_boot_report_t * r,
    1249           6 :                       fd_topo_t const *  topo ) {
    1250           6 :   char * cur = r->topology_json;
    1251           6 :   ulong  rem = sizeof(r->topology_json);
    1252             : 
    1253         108 : #define PRINT( ... ) do {                                          \
    1254          84 :     ulong _n;                                                      \
    1255          84 :     FD_TEST( fd_cstr_printf_check( cur, rem, &_n, __VA_ARGS__ ) ); \
    1256          84 :     cur += _n; rem -= _n;                                          \
    1257          84 :   } while( 0 )
    1258             : 
    1259           6 :   PRINT( "{\"tiles\":[" );
    1260          18 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
    1261          12 :     fd_topo_tile_t const * tile = &topo->tiles[ i ];
    1262          12 :     PRINT( "%s{\"kind\":\"%s\",\"kind_id\":%lu,\"cpu_idx\":%ld,\"floats\":%s,\"in\":[",
    1263          12 :            i ? "," : "", tile->name, tile->kind_id,
    1264          12 :            tile->cpu_idx<FD_TILE_MAX ? (long)tile->cpu_idx : -1L, tile->floats ? "true" : "false" );
    1265          18 :     for( ulong in=0UL; in<tile->in_cnt; in++ ) PRINT( "%s%lu", in ? "," : "", tile->in_link_id[ in ] );
    1266          12 :     PRINT( "],\"out\":[" );
    1267          18 :     for( ulong out=0UL; out<tile->out_cnt; out++ ) PRINT( "%s%lu", out ? "," : "", tile->out_link_id[ out ] );
    1268          12 :     PRINT( "],\"event_link\":%ld,\"obj_cnt\":%lu}",
    1269          12 :            tile->event_link_id!=ULONG_MAX ? (long)tile->event_link_id : -1L, tile->uses_obj_cnt );
    1270          12 :   }
    1271           6 :   PRINT( "],\"links\":[" );
    1272          12 :   for( ulong i=0UL; i<topo->link_cnt; i++ ) {
    1273             :     /* referenced from the tile in/out lists by array position */
    1274           6 :     fd_topo_link_t const * link = &topo->links[ i ];
    1275           6 :     PRINT( "%s{\"kind\":\"%s\",\"kind_id\":%lu,\"depth\":%lu,\"mtu\":%lu,\"burst\":%lu}",
    1276           6 :            i ? "," : "", link->name, link->kind_id, link->depth, link->mtu, link->burst );
    1277           6 :   }
    1278           6 :   PRINT( "],\"workspaces\":[" );
    1279          12 :   for( ulong i=0UL; i<topo->wksp_cnt; i++ ) {
    1280           6 :     fd_topo_wksp_t const * wksp = &topo->workspaces[ i ];
    1281           6 :     PRINT( "%s{\"name\":\"%s\",\"numa_idx\":%lu,\"page_sz\":%lu,\"page_cnt\":%lu,\"footprint\":%lu,\"loose\":%lu}",
    1282           6 :            i ? "," : "", wksp->name, wksp->numa_idx, wksp->page_sz, wksp->page_cnt,
    1283           6 :            wksp->known_footprint, wksp->total_footprint-wksp->known_footprint );
    1284           6 :   }
    1285           6 :   PRINT( "]}" );
    1286             : 
    1287           6 : #undef PRINT
    1288             : 
    1289           6 :   r->topology_json_len = (ulong)( cur-r->topology_json );
    1290           6 : }
    1291             : 
    1292             : /* protobuf encode: field numbers are 1-based positions in
    1293             :    schema/boot.json, tuple subfield numbers 1-based positions within
    1294             :    the tuple, enum values 1-based variant positions */
    1295             : 
    1296             : static int
    1297             : push_cstr( fd_pb_encoder_t * e,
    1298             :            uint              id,
    1299        5378 :            char const *      s ) {
    1300        5378 :   ulong len = strlen( s );
    1301        5378 :   if( !len ) return 1;
    1302        5342 :   char clean[ 512 ];
    1303        5342 :   FD_TEST( len<sizeof(clean) );
    1304      176034 :   for( ulong i=0UL; i<len; i++ ) clean[ i ] = ( s[ i ]<0x20 || s[ i ]>0x7e ) ? '?' : s[ i ];
    1305        5342 :   return !!fd_pb_push_bytes( e, id, clean, len );
    1306        5342 : }
    1307             : 
    1308             : void
    1309             : fd_boot_report_publish( fd_boot_report_t *  r,
    1310             :                         fd_topo_t const *   topo,
    1311             :                         fd_circq_t *        circq,
    1312           6 :                         fd_event_client_t * client ) {
    1313           6 :   render_topology_json( r, topo );
    1314             : 
    1315           6 :   r->tile_count = (ushort)fd_ulong_min( topo->tile_cnt, USHORT_MAX );
    1316           6 :   ulong stack_bytes  = topo->tile_cnt*FD_SHMEM_HUGE_PAGE_SZ*( (FD_TILE_PRIVATE_STACK_SZ/FD_SHMEM_HUGE_PAGE_SZ)+2UL );
    1317           6 :   ulong normal_bytes = fd_topo_normal_page_cnt( topo )*FD_SHMEM_NORMAL_PAGE_SZ;
    1318           6 :   r->memory_total = fd_topo_mlock( topo )+stack_bytes+normal_bytes;
    1319          12 :   for( ulong i=0UL; i<fd_shmem_numa_cnt(); i++ ) {
    1320           6 :     r->memory_gigantic_pages += fd_topo_gigantic_page_cnt( topo, i );
    1321           6 :     r->memory_huge_pages     += fd_topo_huge_page_cnt( topo, i, 0 );
    1322           6 :   }
    1323           6 :   r->memory_normal_pages = fd_topo_normal_page_cnt( topo );
    1324             : 
    1325           6 :   uchar * buffer = fd_circq_push_back( circq, 1UL, FD_EVENT_BOOT_BUF_MAX );
    1326           6 :   FD_TEST( buffer );
    1327             : 
    1328           6 :   ulong event_id = fd_event_client_id_reserve( client );
    1329             : 
    1330           6 :   fd_pb_encoder_t encoder[1];
    1331           6 :   fd_pb_encoder_init( encoder, buffer, FD_EVENT_BOOT_BUF_MAX );
    1332             : 
    1333           6 :   int ok = 1;
    1334             : 
    1335           6 :   FD_TEST( circq->cursor_push_seq );
    1336           6 :   ok &= !!fd_pb_push_uint64( encoder, 1U, circq->cursor_push_seq-1UL );
    1337           6 :   ok &= !!fd_pb_push_uint64( encoder, 2U, event_id );
    1338           6 :   ok &= !!fd_pb_push_uint64( encoder, 3U, 0UL ); /* link_seq */
    1339           6 :   ok &= !!fd_pb_push_uint64( encoder, 4U, (ulong)fd_log_wallclock() );
    1340             : 
    1341           6 :   ok &= !!fd_pb_submsg_open( encoder, 5U );  /* Event */
    1342           6 :   ok &= !!fd_pb_submsg_open( encoder, 10U ); /* Boot */
    1343             : 
    1344           6 :   ok &= push_cstr( encoder, 1U, r->kernel_release );
    1345           6 :   ok &= push_cstr( encoder, 2U, r->kernel_version );
    1346         120 :   for( ulong i=0UL; i<r->mitigations_cnt; i++ ) ok &= push_cstr( encoder, 3U, r->mitigations[ i ] );
    1347           6 :   ok &= push_cstr( encoder, 4U, r->distro_id );
    1348           6 :   ok &= push_cstr( encoder, 5U, r->distro_version_id );
    1349           6 :   ok &= push_cstr( encoder, 6U, r->distro_id_like );
    1350           6 :   if( r->libc_kind ) ok &= !!fd_pb_push_int32( encoder, 7U, r->libc_kind );
    1351           6 :   ok &= push_cstr( encoder, 8U, r->libc_version );
    1352           6 :   if( r->build_machine_target ) ok &= !!fd_pb_push_int32( encoder, 9U, r->build_machine_target );
    1353           6 :   ok &= push_cstr( encoder, 10U, r->build_extras );
    1354           6 :   if( r->build_git_dirty ) ok &= !!fd_pb_push_bool( encoder, 11U, r->build_git_dirty );
    1355           6 :   if( r->compiler ) ok &= !!fd_pb_push_int32( encoder, 12U, r->compiler );
    1356           6 :   ok &= push_cstr( encoder, 13U, r->compiler_version );
    1357           6 :   if( r->build_timestamp_nanos ) ok &= !!fd_pb_push_uint64( encoder, 14U, r->build_timestamp_nanos );
    1358          65 :   for( ulong i=0UL; i<r->build_features_cnt; i++ ) ok &= push_cstr( encoder, 15U, r->build_features[ i ] );
    1359             : 
    1360           6 :   if( r->cpu_arch )   ok &= !!fd_pb_push_int32( encoder, 16U, r->cpu_arch );
    1361           6 :   if( r->cpu_vendor ) ok &= !!fd_pb_push_int32( encoder, 17U, r->cpu_vendor );
    1362           6 :   ok &= push_cstr( encoder, 18U, r->cpu_model_name );
    1363           6 :   if( r->cpu_family )            ok &= !!fd_pb_push_uint32( encoder, 19U, r->cpu_family );
    1364           6 :   if( r->cpu_model_id )          ok &= !!fd_pb_push_uint32( encoder, 20U, r->cpu_model_id );
    1365           6 :   if( r->cpu_stepping )          ok &= !!fd_pb_push_uint32( encoder, 21U, r->cpu_stepping );
    1366           6 :   if( r->cpu_microcode_version ) ok &= !!fd_pb_push_uint64( encoder, 22U, r->cpu_microcode_version );
    1367           6 :   if( r->cpu_logical_count )       ok &= !!fd_pb_push_uint32( encoder, 23U, r->cpu_logical_count );
    1368           6 :   if( r->cpu_physical_core_count ) ok &= !!fd_pb_push_uint32( encoder, 24U, r->cpu_physical_core_count );
    1369           6 :   if( r->cpu_socket_count )        ok &= !!fd_pb_push_uint32( encoder, 25U, r->cpu_socket_count );
    1370           6 :   if( r->smt_active )          ok &= !!fd_pb_push_bool( encoder, 26U, 1 );
    1371           6 :   if( r->cpufreq_governor )           ok &= !!fd_pb_push_int32( encoder, 27U, r->cpufreq_governor );
    1372           6 :   if( r->cpufreq_driver )             ok &= !!fd_pb_push_int32( encoder, 28U, r->cpufreq_driver );
    1373           6 :   if( r->cpu_energy_perf_preference ) ok &= !!fd_pb_push_int32( encoder, 29U, r->cpu_energy_perf_preference );
    1374           6 :   if( r->cpu_idle_driver )            ok &= !!fd_pb_push_int32( encoder, 30U, r->cpu_idle_driver );
    1375           6 :   ok &= push_cstr( encoder, 31U, r->cpu_max_enabled_cstate );
    1376             : 
    1377           6 :   if( r->host_memory_bytes )     ok &= !!fd_pb_push_uint64( encoder, 32U, r->host_memory_bytes );
    1378           6 :   if( r->host_memory_available_bytes ) ok &= !!fd_pb_push_uint64( encoder, 33U, r->host_memory_available_bytes );
    1379        3366 :   for( ulong i=0UL; i<r->numa_cpu_to_node_cnt; i++ ) ok &= !!fd_pb_push_uint32( encoder, 34U, r->numa_cpu_to_node[ i ] );
    1380        3078 :   for( ulong i=0UL; i<r->isolated_cpus_cnt; i++ )    ok &= !!fd_pb_push_uint32( encoder, 35U, r->isolated_cpus[ i ] );
    1381             : 
    1382         198 :   for( ulong i=0UL; i<r->dmi_dimms_cnt; i++ ) {
    1383         192 :     fd_boot_report_dimm_t const * d = &r->dmi_dimms[ i ];
    1384         192 :     ok &= !!fd_pb_submsg_open( encoder, 36U );
    1385         192 :     if( d->size_bytes ) ok &= !!fd_pb_push_uint64( encoder, 1U, d->size_bytes );
    1386         192 :     ok &= push_cstr( encoder, 2U, d->mem_type );
    1387         192 :     if( d->speed_mts )       ok &= !!fd_pb_push_uint64( encoder, 3U, d->speed_mts );
    1388         192 :     if( d->rated_speed_mts ) ok &= !!fd_pb_push_uint64( encoder, 4U, d->rated_speed_mts );
    1389         192 :     ok &= push_cstr( encoder, 5U, d->manufacturer );
    1390         192 :     ok &= push_cstr( encoder, 6U, d->part_number );
    1391         192 :     ok &= !!fd_pb_submsg_close( encoder );
    1392         192 :   }
    1393             : 
    1394          69 :   for( ulong i=0UL; i<r->nic_devices_cnt; i++ ) {
    1395          63 :     fd_boot_report_nic_t const * n = &r->nic_devices[ i ];
    1396          63 :     ok &= !!fd_pb_submsg_open( encoder, 37U );
    1397          63 :     ok &= push_cstr( encoder, 1U, n->name );
    1398          63 :     ok &= push_cstr( encoder, 2U, n->driver );
    1399          63 :     ok &= push_cstr( encoder, 3U, n->driver_version );
    1400          63 :     ok &= push_cstr( encoder, 4U, n->firmware_version );
    1401          63 :     if( n->pcie_current_link_width ) ok &= !!fd_pb_push_uint32( encoder, 5U, n->pcie_current_link_width );
    1402          63 :     if( n->pcie_max_link_width )     ok &= !!fd_pb_push_uint32( encoder, 6U, n->pcie_max_link_width );
    1403          63 :     ok &= push_cstr( encoder, 7U, n->pcie_current_link_speed );
    1404          63 :     ok &= push_cstr( encoder, 8U, n->pcie_max_link_speed );
    1405          63 :     ok &= push_cstr( encoder, 9U, n->bus_info );
    1406          63 :     if( n->pci_device_id )           ok &= !!fd_pb_push_uint32( encoder, 10U, n->pci_device_id );
    1407          63 :     if( n->pci_vendor_id )           ok &= !!fd_pb_push_uint32( encoder, 11U, n->pci_vendor_id );
    1408          63 :     if( n->pci_subsystem_vendor_id ) ok &= !!fd_pb_push_uint32( encoder, 12U, n->pci_subsystem_vendor_id );
    1409          63 :     if( n->pci_subsystem_device_id ) ok &= !!fd_pb_push_uint32( encoder, 13U, n->pci_subsystem_device_id );
    1410          63 :     if( n->numa_node )       ok &= !!fd_pb_push_uint64( encoder, 14U, n->numa_node );
    1411          63 :     if( n->mtu )             ok &= !!fd_pb_push_uint64( encoder, 15U, n->mtu );
    1412          63 :     if( n->link_speed_mbps ) ok &= !!fd_pb_push_uint64( encoder, 16U, n->link_speed_mbps );
    1413          63 :     if( n->carrier )     ok &= !!fd_pb_push_bool( encoder, 17U, 1 );
    1414          63 :     if( n->promiscuous ) ok &= !!fd_pb_push_bool( encoder, 18U, 1 );
    1415          63 :     if( n->channels_current ) ok &= !!fd_pb_push_uint32( encoder, 19U, n->channels_current );
    1416          63 :     if( n->channels_max )     ok &= !!fd_pb_push_uint32( encoder, 20U, n->channels_max );
    1417          63 :     if( n->rx_ring_size ) ok &= !!fd_pb_push_uint64( encoder, 21U, n->rx_ring_size );
    1418          63 :     if( n->tx_ring_size ) ok &= !!fd_pb_push_uint64( encoder, 22U, n->tx_ring_size );
    1419          63 :     if( n->rx_ring_max )  ok &= !!fd_pb_push_uint64( encoder, 23U, n->rx_ring_max );
    1420          63 :     if( n->tx_ring_max )  ok &= !!fd_pb_push_uint64( encoder, 24U, n->tx_ring_max );
    1421        3426 :     for( ulong k=0UL; k<n->offloads_cnt; k++ ) ok &= push_cstr( encoder, 25U, n->offloads[ k ] );
    1422          63 :     if( n->rss_flow_hash_udp4 )  ok &= !!fd_pb_push_bool( encoder, 26U, 1 );
    1423          63 :     if( n->coalesce_rx_usecs )    ok &= !!fd_pb_push_uint64( encoder, 27U, n->coalesce_rx_usecs );
    1424          63 :     if( n->coalesce_adaptive_rx ) ok &= !!fd_pb_push_bool( encoder, 28U, 1 );
    1425          63 :     ok &= push_cstr( encoder, 29U, n->rss_hash_func );
    1426          63 :     if( n->ntuple_rule_count ) ok &= !!fd_pb_push_uint64( encoder, 30U, n->ntuple_rule_count );
    1427          63 :     if( n->is_bond_master )    ok &= !!fd_pb_push_bool( encoder, 31U, 1 );
    1428          63 :     ok &= !!fd_pb_submsg_close( encoder );
    1429          63 :   }
    1430             : 
    1431          54 :   for( ulong i=0UL; i<r->nic_bond_slaves_cnt; i++ ) ok &= push_cstr( encoder, 38U, r->nic_bond_slaves[ i ] );
    1432           6 :   if( r->bond_mode ) ok &= !!fd_pb_push_int32( encoder, 39U, r->bond_mode );
    1433           6 :   if( r->bound_nic_idx ) ok &= !!fd_pb_push_uint32( encoder, 40U, r->bound_nic_idx );
    1434           6 :   if( r->net_bpf_jit_enable ) ok &= !!fd_pb_push_uint32( encoder, 41U, r->net_bpf_jit_enable );
    1435             : 
    1436           6 :   ok &= push_cstr( encoder, 42U, r->dmi_sys_vendor );
    1437           6 :   ok &= push_cstr( encoder, 43U, r->dmi_product_name );
    1438           6 :   ok &= push_cstr( encoder, 44U, r->dmi_bios_version );
    1439           6 :   ok &= push_cstr( encoder, 45U, r->dmi_bios_date );
    1440           6 :   ok &= push_cstr( encoder, 46U, r->dmi_board_vendor );
    1441           6 :   ok &= push_cstr( encoder, 47U, r->dmi_board_name );
    1442           6 :   if( r->dmi_chassis_type )  ok &= !!fd_pb_push_int32( encoder, 48U, r->dmi_chassis_type );
    1443           6 :   if( r->iommu_mode )        ok &= !!fd_pb_push_int32( encoder, 49U, r->iommu_mode );
    1444           6 :   if( r->hypervisor_vendor ) ok &= !!fd_pb_push_int32( encoder, 50U, r->hypervisor_vendor );
    1445           6 :   if( r->container_type ) ok &= !!fd_pb_push_int32( encoder, 51U, r->container_type );
    1446           6 :   if( r->kubernetes_pod ) ok &= !!fd_pb_push_bool( encoder, 52U, 1 );
    1447           6 :   if( r->cgroup_memory_limit_bytes )   ok &= !!fd_pb_push_uint64( encoder, 53U, r->cgroup_memory_limit_bytes );
    1448           6 :   if( r->cgroup_memory_high_bytes )    ok &= !!fd_pb_push_uint64( encoder, 54U, r->cgroup_memory_high_bytes );
    1449           6 :   if( r->cgroup_swap_limit_bytes )     ok &= !!fd_pb_push_uint64( encoder, 55U, r->cgroup_swap_limit_bytes );
    1450           6 :   if( r->cgroup_cpu_quota_millicores ) ok &= !!fd_pb_push_uint32( encoder, 56U, r->cgroup_cpu_quota_millicores );
    1451             : 
    1452         102 :   for( ulong i=0UL; i<r->block_devices_cnt; i++ ) {
    1453          96 :     fd_boot_report_block_device_t const * d = &r->block_devices[ i ];
    1454          96 :     ok &= !!fd_pb_submsg_open( encoder, 57U );
    1455          96 :     ok &= push_cstr( encoder, 1U, d->name );
    1456          96 :     ok &= push_cstr( encoder, 2U, d->model );
    1457          96 :     if( d->capacity_bytes ) ok &= !!fd_pb_push_uint64( encoder, 3U, d->capacity_bytes );
    1458          96 :     if( d->rotational )     ok &= !!fd_pb_push_bool( encoder, 4U, 1 );
    1459          96 :     ok &= push_cstr( encoder, 5U, d->transport );
    1460          96 :     ok &= push_cstr( encoder, 6U, d->scheduler );
    1461          96 :     ok &= !!fd_pb_submsg_close( encoder );
    1462          96 :   }
    1463             : 
    1464          66 :   for( ulong i=0UL; i<r->nvme_devices_cnt; i++ ) {
    1465          60 :     fd_boot_report_nvme_t const * d = &r->nvme_devices[ i ];
    1466          60 :     ok &= !!fd_pb_submsg_open( encoder, 58U );
    1467          60 :     if( d->device_idx ) ok &= !!fd_pb_push_uint32( encoder, 1U, d->device_idx );
    1468          60 :     ok &= push_cstr( encoder, 2U, d->firmware_rev );
    1469          60 :     if( d->critical_warning )    ok &= !!fd_pb_push_uint32( encoder, 3U, d->critical_warning );
    1470          60 :     if( d->available_spare_pct ) ok &= !!fd_pb_push_uint32( encoder, 4U, d->available_spare_pct );
    1471          60 :     if( d->percentage_used )     ok &= !!fd_pb_push_uint32( encoder, 5U, d->percentage_used );
    1472          60 :     if( d->media_errors )        ok &= !!fd_pb_push_uint64( encoder, 6U, d->media_errors );
    1473          60 :     if( d->numa_node )           ok &= !!fd_pb_push_uint32( encoder, 7U, d->numa_node );
    1474          60 :     if( d->namespace_size_bytes ) ok &= !!fd_pb_push_uint64( encoder, 8U, d->namespace_size_bytes );
    1475          60 :     ok &= !!fd_pb_submsg_close( encoder );
    1476          60 :   }
    1477             : 
    1478          66 :   for( ulong i=0UL; i<r->mdraid_arrays_cnt; i++ ) {
    1479          60 :     fd_boot_report_mdraid_t const * d = &r->mdraid_arrays[ i ];
    1480          60 :     ok &= !!fd_pb_submsg_open( encoder, 59U );
    1481          60 :     ok &= push_cstr( encoder, 1U, d->name );
    1482          60 :     ok &= push_cstr( encoder, 2U, d->level );
    1483          60 :     if( d->device_count ) ok &= !!fd_pb_push_uint32( encoder, 3U, d->device_count );
    1484          60 :     if( d->degraded )     ok &= !!fd_pb_push_bool( encoder, 4U, 1 );
    1485          60 :     ok &= !!fd_pb_submsg_close( encoder );
    1486          60 :   }
    1487             : 
    1488          33 :   for( ulong i=0UL; i<r->filesystems_cnt; i++ ) {
    1489          27 :     fd_boot_report_fs_t const * f = &r->filesystems[ i ];
    1490          27 :     ok &= !!fd_pb_submsg_open( encoder, 60U );
    1491          27 :     if( f->device_idx ) ok &= !!fd_pb_push_uint32( encoder, 1U, f->device_idx );
    1492          27 :     ok &= push_cstr( encoder, 2U, f->fs_type );
    1493          27 :     ok &= push_cstr( encoder, 3U, f->mount_options );
    1494          27 :     if( f->capacity_bytes ) ok &= !!fd_pb_push_uint64( encoder, 4U, f->capacity_bytes );
    1495          27 :     if( f->free_bytes )     ok &= !!fd_pb_push_uint64( encoder, 5U, f->free_bytes );
    1496          27 :     ok &= !!fd_pb_submsg_close( encoder );
    1497          27 :   }
    1498             : 
    1499           6 :   if( r->accounts_fs_idx )  ok &= !!fd_pb_push_uint32( encoder, 61U, r->accounts_fs_idx );
    1500           6 :   if( r->snapshots_fs_idx ) ok &= !!fd_pb_push_uint32( encoder, 62U, r->snapshots_fs_idx );
    1501           6 :   if( r->log_fs_idx )       ok &= !!fd_pb_push_uint32( encoder, 63U, r->log_fs_idx );
    1502           6 :   if( r->shredb_fs_idx )    ok &= !!fd_pb_push_uint32( encoder, 64U, r->shredb_fs_idx );
    1503           6 :   if( r->guidb_fs_idx )     ok &= !!fd_pb_push_uint32( encoder, 65U, r->guidb_fs_idx );
    1504             : 
    1505           6 :   if( r->resolved_config_json_len ) ok &= !!fd_pb_push_bytes( encoder, 66U, r->resolved_config_json, r->resolved_config_json_len );
    1506           6 :   if( r->user_config_json_len )     ok &= !!fd_pb_push_bytes( encoder, 67U, r->user_config_json,     r->user_config_json_len );
    1507           6 :   if( r->topology_json_len ) ok &= !!fd_pb_push_bytes( encoder, 68U, r->topology_json, r->topology_json_len );
    1508           6 :   if( r->tile_count )                ok &= !!fd_pb_push_uint32( encoder, 69U, r->tile_count );
    1509           6 :   if( r->memory_total ) ok &= !!fd_pb_push_uint64( encoder, 70U, r->memory_total );
    1510           6 :   if( r->memory_gigantic_pages )   ok &= !!fd_pb_push_uint64( encoder, 71U, r->memory_gigantic_pages );
    1511           6 :   if( r->memory_huge_pages )       ok &= !!fd_pb_push_uint64( encoder, 72U, r->memory_huge_pages );
    1512           6 :   if( r->memory_normal_pages )     ok &= !!fd_pb_push_uint64( encoder, 73U, r->memory_normal_pages );
    1513           6 :   if( r->process_start_time_nanos ) ok &= !!fd_pb_push_uint64( encoder, 74U, r->process_start_time_nanos );
    1514           6 :   if( r->feature_set_id )           ok &= !!fd_pb_push_uint32( encoder, 75U, r->feature_set_id );
    1515             : 
    1516           6 :   ok &= !!fd_pb_submsg_close( encoder );
    1517           6 :   ok &= !!fd_pb_submsg_close( encoder );
    1518           6 :   FD_TEST( ok );
    1519           6 :   fd_circq_resize_back( circq, fd_pb_encoder_out_sz( encoder ) );
    1520           6 : }

Generated by: LCOV version 1.14