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