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

Generated by: LCOV version 1.14