Line data Source code
1 : #define _GNU_SOURCE
2 : #include "fd_bootinfo.h"
3 :
4 : #include "../../util/fd_version.h"
5 : #include "../../util/pod/fd_pod.h"
6 : #include "../../util/sandbox/fd_sandbox.h"
7 : #include "../../util/shmem/fd_shmem_private.h"
8 : #include "../../tango/tempo/fd_tempo.h"
9 :
10 : #include <dirent.h>
11 : #include <errno.h>
12 : #include <fcntl.h>
13 : #include <limits.h>
14 : #include <stdio.h>
15 : #include <string.h>
16 : #include <sys/resource.h>
17 : #include <sys/stat.h>
18 : #include <unistd.h>
19 :
20 0 : #define BOOTINFO_NORMAL "\033[0m"
21 0 : #define BOOTINFO_BOLD "\033[1m"
22 0 : #define BOOTINFO_DIM "\033[2m"
23 0 : #define BOOTINFO_GREEN "\033[32m"
24 0 : #define BOOTINFO_YELLOW "\033[93m"
25 :
26 : /* proc_start_time reads /proc/<pid>/stat field 22 (starttime, in
27 : clock ticks since boot). Returns 0 on failure (0 is not a valid
28 : start time for any process we care about). The comm field can
29 : contain spaces and parens, so parse from the last ')'. */
30 :
31 : static ulong
32 0 : proc_start_time( ulong pid ) {
33 0 : char path[ 64UL ];
34 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/proc/%lu/stat", pid ) );
35 :
36 0 : char buf[ 4096UL ];
37 0 : int fd = open( path, O_RDONLY );
38 0 : if( FD_UNLIKELY( -1==fd ) ) return 0UL;
39 0 : long sz = read( fd, buf, sizeof(buf)-1UL );
40 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
41 0 : if( FD_UNLIKELY( sz<=0L ) ) return 0UL;
42 0 : buf[ sz ] = '\0';
43 :
44 0 : char * p = strrchr( buf, ')' );
45 0 : if( FD_UNLIKELY( !p ) ) return 0UL;
46 :
47 : /* fields 3 (state) through 21 follow ')', starttime is field 22 */
48 0 : ulong start_time = 0UL;
49 0 : if( FD_UNLIKELY( 1!=sscanf( p+1UL, " %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu", &start_time ) ) ) return 0UL;
50 0 : return start_time;
51 0 : }
52 :
53 : static void
54 : bootinfo_path( char const * mount_path,
55 : char const * name,
56 0 : char * out ) {
57 0 : FD_TEST( fd_cstr_printf_check( out, PATH_MAX, NULL, "%s/%s.bootinfo", mount_path, name ) );
58 0 : }
59 :
60 : /* write_file_atomic writes sz bytes at data to path via a same-dir
61 : .partial rename. Returns 0 on success, -1 otherwise. */
62 :
63 : static int
64 : write_file_atomic( char const * path,
65 : void const * data,
66 : ulong sz,
67 0 : mode_t mode ) {
68 0 : char partial_path[ PATH_MAX ];
69 0 : if( FD_UNLIKELY( !fd_cstr_printf_check( partial_path, sizeof(partial_path), NULL, "%s.partial", path ) ) ) return -1;
70 :
71 0 : int fd = open( partial_path, O_WRONLY|O_CREAT|O_TRUNC, mode );
72 0 : if( FD_UNLIKELY( -1==fd ) ) return -1;
73 :
74 0 : uchar const * p = (uchar const *)data;
75 0 : ulong remaining = sz;
76 0 : while( remaining ) {
77 0 : long written = write( fd, p, remaining );
78 0 : if( FD_UNLIKELY( written<=0L ) ) {
79 0 : if( FD_UNLIKELY( written<0L && errno==EINTR ) ) continue;
80 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
81 0 : if( FD_UNLIKELY( -1==unlink( partial_path ) && errno!=ENOENT ) ) FD_LOG_WARNING(( "unlink() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
82 0 : return -1;
83 0 : }
84 0 : p += written; remaining -= (ulong)written;
85 0 : }
86 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
87 :
88 0 : if( FD_UNLIKELY( -1==rename( partial_path, path ) ) ) return -1;
89 0 : return 0;
90 0 : }
91 :
92 : void
93 0 : fd_bootinfo_write( config_t const * config ) {
94 0 : fd_bootinfo_t info = {0};
95 0 : info.magic = FD_BOOTINFO_MAGIC;
96 0 : info.version = FD_BOOTINFO_VERSION;
97 0 : info.pid = fd_sandbox_getpid();
98 0 : info.pid_start_time = proc_start_time( info.pid );
99 0 : info.boot_wallclock_nanos = fd_log_wallclock();
100 0 : fd_cstr_ncpy( info.commit_ref, fd_commit_ref_cstr, sizeof(info.commit_ref) );
101 0 : info.fd_version[ 0 ] = fd_major_version;
102 0 : info.fd_version[ 1 ] = fd_minor_version;
103 0 : info.fd_version[ 2 ] = fd_patch_version;
104 0 : fd_cstr_ncpy( info.name, config->name, sizeof(info.name) );
105 0 : info.uid = config->uid;
106 0 : info.gid = config->gid;
107 0 : info.topo_layout_hash = config->topo.layout_hash;
108 :
109 0 : ulong adminctl_obj_id = fd_pod_query_ulong( config->topo.props, "adminctl", ULONG_MAX );
110 0 : if( FD_LIKELY( adminctl_obj_id!=ULONG_MAX ) ) {
111 0 : fd_topo_obj_t const * obj = &config->topo.objs[ adminctl_obj_id ];
112 0 : fd_topo_wksp_t const * wksp = &config->topo.workspaces[ obj->wksp_id ];
113 0 : FD_TEST( fd_cstr_printf_check( info.adminctl_wksp_file, sizeof(info.adminctl_wksp_file), NULL, "%s_%s.wksp", config->name, wksp->name ) );
114 0 : info.adminctl_page_sz = wksp->page_sz;
115 0 : info.adminctl_offset = obj->offset;
116 0 : }
117 :
118 0 : if( FD_UNLIKELY( !info.pid_start_time ) ) {
119 0 : FD_LOG_WARNING(( "could not read own process start time, validator discovery will be unavailable" ));
120 0 : return;
121 0 : }
122 :
123 : /* Publish the resolved config blob first so a reader that sees the
124 : descriptor can always recover it. Contains paths and settings, no
125 : key material. Sanitize raw user config. */
126 0 : config_t * mut = (config_t *)config;
127 0 : static char saved_toml[ sizeof(config->user_config) ];
128 0 : ulong saved_toml_len = config->user_config_len;
129 0 : fd_memcpy( saved_toml, mut->user_config, sizeof(saved_toml) );
130 0 : fd_memset( mut->user_config, 0, sizeof(mut->user_config) );
131 0 : mut->user_config_len = 0UL;
132 :
133 0 : char blob_path[ PATH_MAX ];
134 0 : FD_TEST( fd_cstr_printf_check( blob_path, sizeof(blob_path), NULL, "%s/%s.config", config->hugetlbfs.mount_path, config->name ) );
135 0 : int blob_err = write_file_atomic( blob_path, config, sizeof(config_t), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH );
136 :
137 0 : fd_memcpy( mut->user_config, saved_toml, sizeof(saved_toml) );
138 0 : mut->user_config_len = saved_toml_len;
139 :
140 0 : if( FD_UNLIKELY( -1==blob_err ) ) {
141 0 : FD_LOG_WARNING(( "write(%s) failed (%i-%s), attaching to this validator will require --config", blob_path, errno, fd_io_strerror( errno ) ));
142 0 : } else {
143 0 : FD_TEST( fd_cstr_printf_check( info.config_file, sizeof(info.config_file), NULL, "%s.config", config->name ) );
144 0 : info.config_sz = sizeof(config_t);
145 0 : }
146 :
147 0 : char path[ PATH_MAX ];
148 0 : bootinfo_path( config->hugetlbfs.mount_path, config->name, path );
149 :
150 0 : if( FD_UNLIKELY( -1==write_file_atomic( path, &info, sizeof(info), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ) ) )
151 0 : FD_LOG_WARNING(( "write(%s) failed (%i-%s), validator discovery will be unavailable", path, errno, fd_io_strerror( errno ) ));
152 :
153 0 : char const * c_normal = fd_log_style_normal();
154 0 : char const * c_bold = fd_log_style_bold();
155 0 : char const * c_dim = fd_log_style_dim();
156 :
157 0 : char version[ 32UL ];
158 0 : fd_version_cstr_format( version, sizeof(version), info.fd_version[ 0 ], info.fd_version[ 1 ], info.fd_version[ 2 ] );
159 :
160 0 : ulong mem_gib = (fd_topo_mlock( &config->topo )+((1UL<<30UL)-1UL))>>30UL;
161 0 : FD_LOG_NOTICE(( "booting validator %s%s%s version %s %s(%.11s)%s pid %lu with %s%lu%s tiles and %s%lu GiB%s memory at %s%s%s",
162 0 : c_bold, info.name, c_normal,
163 0 : version,
164 0 : c_dim, info.commit_ref, c_normal,
165 0 : info.pid,
166 0 : c_bold, config->topo.tile_cnt, c_normal,
167 0 : c_bold, mem_gib, c_normal,
168 0 : c_dim, config->hugetlbfs.mount_path, c_normal ));
169 0 : }
170 :
171 : void
172 0 : fd_bootinfo_unlink( config_t const * config ) {
173 0 : char const * suffix[ 4 ] = { "bootinfo", "bootinfo.partial", "config", "config.partial" };
174 0 : for( ulong i=0UL; i<4UL; i++ ) {
175 0 : char path[ PATH_MAX ];
176 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s.%s", config->hugetlbfs.mount_path, config->name, suffix[ i ] ) );
177 0 : if( FD_UNLIKELY( -1==unlink( path ) && errno!=ENOENT ) )
178 0 : FD_LOG_WARNING(( "unlink(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
179 0 : }
180 0 : }
181 :
182 : int
183 : fd_bootinfo_path_read( char const * path,
184 0 : fd_bootinfo_t * out ) {
185 0 : int fd = open( path, O_RDONLY );
186 0 : if( FD_UNLIKELY( -1==fd ) ) return -1;
187 :
188 0 : struct stat st;
189 0 : if( FD_UNLIKELY( -1==fstat( fd, &st ) ) ) goto fail;
190 0 : if( FD_UNLIKELY( !S_ISREG( st.st_mode ) ) ) goto fail;
191 0 : if( FD_UNLIKELY( st.st_uid && st.st_uid!=geteuid() ) ) goto fail; /* only trust root or self */
192 0 : if( FD_UNLIKELY( st.st_mode & S_IWOTH ) ) goto fail;
193 : /* Newer builds may append fields, the v1 prefix is frozen so any
194 : genuine descriptor is at least this large and parses below. */
195 0 : if( FD_UNLIKELY( st.st_size<(long)sizeof(*out) ) ) goto fail;
196 :
197 0 : if( FD_UNLIKELY( sizeof(*out)!=(ulong)read( fd, out, sizeof(*out) ) ) ) goto fail;
198 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
199 :
200 0 : if( FD_UNLIKELY( out->magic!=FD_BOOTINFO_MAGIC ) ) return -1;
201 0 : if( FD_UNLIKELY( !out->version ) ) return -1;
202 0 : if( FD_UNLIKELY( !memchr( out->name, '\0', sizeof(out->name) ) ) ) return -1;
203 0 : if( FD_UNLIKELY( !memchr( out->commit_ref, '\0', sizeof(out->commit_ref) ) ) ) return -1;
204 0 : if( FD_UNLIKELY( !memchr( out->adminctl_wksp_file, '\0', sizeof(out->adminctl_wksp_file) ) ) ) return -1;
205 0 : if( FD_UNLIKELY( strchr( out->adminctl_wksp_file, '/' ) ) ) return -1;
206 0 : if( FD_UNLIKELY( !memchr( out->config_file, '\0', sizeof(out->config_file) ) ) ) return -1;
207 0 : if( FD_UNLIKELY( strchr( out->config_file, '/' ) ) ) return -1;
208 0 : return 0;
209 :
210 0 : fail:
211 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
212 0 : return -1;
213 0 : }
214 :
215 : int
216 0 : fd_bootinfo_live( fd_bootinfo_t const * info ) {
217 0 : ulong start_time = proc_start_time( info->pid );
218 0 : return start_time && start_time==info->pid_start_time;
219 0 : }
220 :
221 : /* mountinfo_unescape decodes \0NN octal escapes in-place. */
222 :
223 : static void
224 0 : mountinfo_unescape( char * s ) {
225 0 : char * w = s;
226 0 : while( *s ) {
227 0 : if( FD_UNLIKELY( s[0]=='\\' && s[1]>='0' && s[1]<='3' && s[2]>='0' && s[2]<='7' && s[3]>='0' && s[3]<='7' ) ) {
228 0 : *w++ = (char)( ((s[1]-'0')<<6) | ((s[2]-'0')<<3) | (s[3]-'0') );
229 0 : s += 4;
230 0 : } else {
231 0 : *w++ = *s++;
232 0 : }
233 0 : }
234 0 : *w = '\0';
235 0 : }
236 :
237 : /* candidate_mounts fills dirs with parent directories of firedancer
238 : shaped hugetlbfs mounts (mountpoint basename .gigantic/.huge/.normal)
239 : plus the default mount path. Returns the count. */
240 :
241 : static ulong
242 : candidate_mounts( char dirs[ FD_BOOTINFO_INSTANCE_MAX ][ PATH_MAX ],
243 0 : ulong max ) {
244 0 : ulong cnt = 0UL;
245 :
246 0 : FILE * fp = fopen( "/proc/self/mountinfo", "r" );
247 0 : if( FD_LIKELY( fp ) ) {
248 0 : char line[ 4096UL ];
249 0 : while( FD_LIKELY( fgets( line, sizeof(line), fp ) ) ) {
250 : /* fields: id parent maj:min root mountpoint opts [optional]* - fstype src superopts */
251 0 : char * sep = strstr( line, " - " );
252 0 : if( FD_UNLIKELY( !sep ) ) continue;
253 :
254 0 : char * fstype = sep+3UL;
255 0 : char * fstype_end = strchr( fstype, ' ' );
256 0 : if( FD_UNLIKELY( !fstype_end ) ) continue;
257 0 : *fstype_end = '\0';
258 0 : if( FD_LIKELY( strcmp( fstype, "hugetlbfs" ) ) ) continue;
259 :
260 0 : *sep = '\0';
261 0 : char * mountpoint = NULL;
262 0 : char * tok = strtok( line, " " );
263 0 : for( ulong i=0UL; tok; i++, tok=strtok( NULL, " " ) ) {
264 0 : if( i==4UL ) { mountpoint = tok; break; }
265 0 : }
266 0 : if( FD_UNLIKELY( !mountpoint ) ) continue;
267 0 : mountinfo_unescape( mountpoint );
268 :
269 0 : char * base = strrchr( mountpoint, '/' );
270 0 : if( FD_UNLIKELY( !base || base==mountpoint ) ) continue;
271 0 : if( FD_LIKELY( strcmp( base, "/.gigantic" ) && strcmp( base, "/.huge" ) && strcmp( base, "/.normal" ) ) ) continue;
272 0 : *base = '\0'; /* mountpoint is now the parent dir */
273 :
274 0 : int seen = 0;
275 0 : for( ulong i=0UL; i<cnt; i++ ) if( !strcmp( dirs[ i ], mountpoint ) ) { seen = 1; break; }
276 0 : if( FD_UNLIKELY( seen ) ) continue;
277 0 : if( FD_UNLIKELY( cnt>=max ) ) break;
278 0 : strncpy( dirs[ cnt ], mountpoint, PATH_MAX-1UL );
279 0 : dirs[ cnt ][ PATH_MAX-1UL ] = '\0';
280 0 : cnt++;
281 0 : }
282 0 : if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_WARNING(( "fclose() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
283 0 : }
284 :
285 : /* default path, covers normal page setups with no hugetlbfs mounts */
286 0 : int seen = 0;
287 0 : for( ulong i=0UL; i<cnt; i++ ) if( !strcmp( dirs[ i ], "/mnt/.fd" ) ) { seen = 1; break; }
288 0 : if( FD_LIKELY( !seen && cnt<max ) ) {
289 0 : strncpy( dirs[ cnt ], "/mnt/.fd", PATH_MAX-1UL );
290 0 : cnt++;
291 0 : }
292 :
293 0 : return cnt;
294 0 : }
295 :
296 : ulong
297 : fd_bootinfo_discover( fd_bootinfo_instance_t * out,
298 0 : ulong max ) {
299 0 : static char dirs[ FD_BOOTINFO_INSTANCE_MAX ][ PATH_MAX ];
300 0 : ulong dir_cnt = candidate_mounts( dirs, FD_BOOTINFO_INSTANCE_MAX );
301 :
302 0 : ulong cnt = 0UL;
303 0 : for( ulong i=0UL; i<dir_cnt && cnt<max; i++ ) {
304 0 : DIR * dir = opendir( dirs[ i ] );
305 0 : if( FD_UNLIKELY( !dir ) ) continue;
306 :
307 0 : struct dirent * entry;
308 0 : while( FD_LIKELY( cnt<max && (entry=readdir( dir )) ) ) {
309 0 : char const * suffix = strstr( entry->d_name, ".bootinfo" );
310 0 : if( FD_LIKELY( !suffix || strcmp( suffix, ".bootinfo" ) ) ) continue;
311 :
312 0 : char path[ PATH_MAX ];
313 0 : if( FD_UNLIKELY( !fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s", dirs[ i ], entry->d_name ) ) ) continue;
314 :
315 0 : fd_bootinfo_t info;
316 0 : if( FD_UNLIKELY( -1==fd_bootinfo_path_read( path, &info ) ) ) continue;
317 :
318 : /* name must match filename */
319 0 : if( FD_UNLIKELY( strncmp( entry->d_name, info.name, (ulong)(suffix-entry->d_name) ) || strlen( info.name )!=(ulong)(suffix-entry->d_name) ) ) continue;
320 :
321 0 : strncpy( out[ cnt ].mount_path, dirs[ i ], PATH_MAX-1UL );
322 0 : out[ cnt ].mount_path[ PATH_MAX-1UL ] = '\0';
323 0 : out[ cnt ].info = info;
324 0 : out[ cnt ].live = fd_bootinfo_live( &info );
325 0 : cnt++;
326 0 : }
327 0 : if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_WARNING(( "closedir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
328 0 : }
329 :
330 0 : return cnt;
331 0 : }
332 :
333 : void
334 : fd_bootinfo_print( fd_bootinfo_instance_t const * instances,
335 0 : ulong cnt ) {
336 : /* fd_log colorize already honors NO_COLOR/TERM/--log-colorize, but is
337 : keyed to stderr, so additionally require stdout to be a tty. */
338 0 : int color = fd_log_colorize() && isatty( STDOUT_FILENO );
339 0 : char const * c_normal = color ? BOOTINFO_NORMAL : "";
340 0 : char const * c_bold = color ? BOOTINFO_BOLD : "";
341 0 : char const * c_dim = color ? BOOTINFO_DIM : "";
342 0 : char const * c_live = color ? BOOTINFO_GREEN : "";
343 0 : char const * c_stale = color ? BOOTINFO_YELLOW : "";
344 :
345 0 : if( FD_UNLIKELY( !cnt ) ) {
346 0 : FD_LOG_STDOUT(( "%sno validators found%s\n", c_dim, c_normal ));
347 0 : return;
348 0 : }
349 :
350 0 : FD_LOG_STDOUT(( "%s%-16s %-8s %-7s %-10s %-12s %-12s %s%s\n", c_dim, "NAME", "PID", "STATE", "UPTIME", "VERSION", "COMMIT", "MOUNT", c_normal ));
351 0 : for( ulong i=0UL; i<cnt; i++ ) {
352 0 : fd_bootinfo_instance_t const * instance = &instances[ i ];
353 0 : fd_bootinfo_t const * info = &instance->info;
354 :
355 0 : char uptime[ 32UL ] = "-";
356 0 : if( FD_LIKELY( instance->live ) ) {
357 0 : long secs = (fd_log_wallclock()-info->boot_wallclock_nanos)/(long)1e9;
358 0 : if( FD_UNLIKELY( secs<0L ) ) secs = 0L;
359 0 : long days = secs/86400L, hours = (secs/3600L)%24L, mins = (secs/60L)%60L;
360 0 : if( FD_UNLIKELY( days ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldd%ldh%ldm", days, hours, mins ) );
361 0 : else if( FD_UNLIKELY( hours ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldh%ldm", hours, mins ) );
362 0 : else if( FD_LIKELY( mins ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldm", mins ) );
363 0 : else FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%lds", secs ) );
364 0 : }
365 :
366 0 : char version[ 32UL ];
367 0 : fd_version_cstr_format( version, sizeof(version), info->fd_version[ 0 ], info->fd_version[ 1 ], info->fd_version[ 2 ] );
368 :
369 0 : char commit[ 12UL ] = {0};
370 0 : strncpy( commit, info->commit_ref, sizeof(commit)-1UL );
371 :
372 0 : FD_LOG_STDOUT(( "%s%-16s%s %-8lu %s%-7s%s %-10s %-12s %-12s %s%s%s\n",
373 0 : c_bold, info->name, c_normal,
374 0 : info->pid,
375 0 : instance->live ? c_live : c_stale,
376 0 : instance->live ? "live" : "stale",
377 0 : c_normal,
378 0 : uptime,
379 0 : version,
380 0 : commit,
381 0 : c_dim, instance->mount_path, c_normal ));
382 0 : }
383 0 : }
384 :
385 : void
386 0 : fd_bootinfo_notice( fd_bootinfo_instance_t const * instance ) {
387 0 : fd_bootinfo_t const * info = &instance->info;
388 :
389 0 : long secs = (fd_log_wallclock()-info->boot_wallclock_nanos)/(long)1e9;
390 0 : if( FD_UNLIKELY( secs<0L ) ) secs = 0L;
391 0 : long days = secs/86400L, hours = (secs/3600L)%24L, mins = (secs/60L)%60L;
392 0 : char uptime[ 32UL ];
393 0 : if( FD_UNLIKELY( days ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldd%ldh%ldm", days, hours, mins ) );
394 0 : else if( FD_UNLIKELY( hours ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldh%ldm", hours, mins ) );
395 0 : else if( FD_LIKELY( mins ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldm", mins ) );
396 0 : else FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%lds", secs ) );
397 :
398 0 : char const * c_normal = fd_log_style_normal();
399 0 : char const * c_bold = fd_log_style_bold();
400 0 : char const * c_dim = fd_log_style_dim();
401 0 : char const * c_live = fd_log_colorize() ? BOOTINFO_GREEN : "";
402 :
403 0 : char version[ 32UL ];
404 0 : fd_version_cstr_format( version, sizeof(version), info->fd_version[ 0 ], info->fd_version[ 1 ], info->fd_version[ 2 ] );
405 :
406 0 : FD_LOG_NOTICE(( "attached to %slive%s validator %s%s%s version %s %s(%.11s)%s pid %lu up %s at %s%s%s",
407 0 : c_live, c_normal,
408 0 : c_bold, info->name, c_normal,
409 0 : version,
410 0 : c_dim, info->commit_ref, c_normal,
411 0 : info->pid,
412 0 : uptime,
413 0 : c_dim, instance->mount_path, c_normal ));
414 0 : }
415 :
416 : void
417 0 : fd_bootinfo_adopt( config_t * config ) {
418 0 : if( FD_LIKELY( config->has_user_config ) ) {
419 : /* config->boot_timestamp_nanos is this command's start time, not
420 : the validator's. Adopt the running validator's, else zero it so
421 : consumers report no uptime rather than a wrong one. */
422 0 : char path[ PATH_MAX ];
423 0 : bootinfo_path( config->hugetlbfs.mount_path, config->name, path );
424 :
425 0 : fd_bootinfo_t info;
426 0 : config->boot_timestamp_nanos = 0L;
427 0 : if( FD_LIKELY( -1!=fd_bootinfo_path_read( path, &info ) && fd_bootinfo_live( &info ) ) ) {
428 0 : config->boot_timestamp_nanos = info.boot_wallclock_nanos;
429 0 : }
430 :
431 0 : return; /* fd_bootinfo_check_layout verifies before joining */
432 0 : }
433 :
434 0 : fd_bootinfo_instance_t instances[ FD_BOOTINFO_INSTANCE_MAX ];
435 0 : ulong cnt = fd_bootinfo_discover( instances, FD_BOOTINFO_INSTANCE_MAX );
436 :
437 0 : fd_bootinfo_instance_t * match = NULL;
438 0 : ulong live_cnt = 0UL;
439 0 : for( ulong i=0UL; i<cnt; i++ ) {
440 0 : if( FD_UNLIKELY( !instances[ i ].live ) ) continue;
441 0 : match = &instances[ i ];
442 0 : live_cnt++;
443 0 : }
444 :
445 0 : if( FD_UNLIKELY( !live_cnt ) ) {
446 0 : if( FD_UNLIKELY( cnt ) ) fd_bootinfo_print( instances, cnt );
447 0 : FD_LOG_ERR(( "No running validator found. If a validator is running, pass --config with the "
448 0 : "configuration file it was started with." ));
449 0 : }
450 :
451 0 : if( FD_UNLIKELY( live_cnt>1UL ) ) {
452 0 : fd_bootinfo_print( instances, cnt );
453 0 : FD_LOG_ERR(( "Multiple running validators found. Pass --config to select one." ));
454 0 : }
455 :
456 0 : fd_bootinfo_t const * info = &match->info;
457 :
458 0 : if( FD_UNLIKELY( strcmp( info->commit_ref, fd_commit_ref_cstr ) ) )
459 0 : FD_LOG_ERR(( "The running validator `%s` is running commit %s but this binary is commit %s. "
460 0 : "This command reads the validator's memory directly and must be run from the "
461 0 : "same binary the validator is running.", info->name, info->commit_ref, fd_commit_ref_cstr ));
462 :
463 0 : if( FD_UNLIKELY( !info->config_file[ 0 ] || info->config_sz!=sizeof(config_t) ) )
464 0 : FD_LOG_ERR(( "The running validator `%s` did not publish a usable configuration. Pass --config "
465 0 : "with the configuration file it was started with.", info->name ));
466 :
467 0 : char path[ PATH_MAX ];
468 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s", match->mount_path, info->config_file ) );
469 :
470 0 : int fd = open( path, O_RDONLY );
471 0 : if( FD_UNLIKELY( -1==fd ) ) FD_LOG_ERR(( "open(%s) failed (%i-%s). The validator `%s` appears to be running "
472 0 : "but its configuration could not be read. Pass --config with the "
473 0 : "configuration file it was started with.", path, errno, fd_io_strerror( errno ), info->name ));
474 :
475 0 : struct stat st;
476 0 : if( FD_UNLIKELY( -1==fstat( fd, &st ) ) ) FD_LOG_ERR(( "fstat(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
477 0 : if( FD_UNLIKELY( !S_ISREG( st.st_mode ) ) ) FD_LOG_ERR(( "`%s` is not a regular file", path ));
478 0 : if( FD_UNLIKELY( st.st_uid && st.st_uid!=geteuid() ) ) FD_LOG_ERR(( "`%s` is not owned by root", path ));
479 0 : if( FD_UNLIKELY( st.st_mode & S_IWOTH ) ) FD_LOG_ERR(( "`%s` is world writable", path ));
480 0 : if( FD_UNLIKELY( st.st_size!=(long)sizeof(config_t) ) ) FD_LOG_ERR(( "validator `%s` config has unexpected size %ld", info->name, st.st_size ));
481 :
482 0 : uchar * p = (uchar *)config;
483 0 : ulong remaining = sizeof(config_t);
484 0 : while( remaining ) {
485 0 : long got = read( fd, p, remaining );
486 0 : if( FD_UNLIKELY( got<=0L ) ) {
487 0 : if( FD_UNLIKELY( got<0L && errno==EINTR ) ) continue;
488 0 : FD_LOG_ERR(( "read(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
489 0 : }
490 0 : p += got; remaining -= (ulong)got;
491 0 : }
492 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
493 :
494 0 : config->has_user_config = 0;
495 :
496 : /* Rebind process state that was derived from the default config
497 : before adoption. */
498 0 : fd_tempo_set_tick_per_ns( config->tick_per_ns_mu, config->tick_per_ns_sigma );
499 :
500 0 : ulong base_len = strlen( config->hugetlbfs.mount_path );
501 0 : if( FD_UNLIKELY( !base_len || base_len>=FD_SHMEM_PRIVATE_BASE_MAX ) ) FD_LOG_ERR(( "adopted invalid mount path" ));
502 0 : memcpy( fd_shmem_private_base, config->hugetlbfs.mount_path, base_len+1UL );
503 0 : fd_shmem_private_base_len = base_len;
504 :
505 : /* Permissions were checked against the default topology, which may
506 : mlock less than the adopted one. Best effort raise. */
507 0 : struct rlimit rl = { .rlim_cur = fd_topo_mlock( &config->topo ), .rlim_max = fd_topo_mlock( &config->topo ) };
508 0 : if( FD_UNLIKELY( -1==setrlimit( RLIMIT_MEMLOCK, &rl ) ) )
509 0 : FD_LOG_INFO(( "setrlimit(RLIMIT_MEMLOCK) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
510 :
511 0 : fd_bootinfo_notice( match );
512 0 : }
513 :
514 : void
515 0 : fd_bootinfo_check_layout( config_t const * config ) {
516 0 : char path[ PATH_MAX ];
517 0 : bootinfo_path( config->hugetlbfs.mount_path, config->name, path );
518 :
519 0 : fd_bootinfo_t info;
520 0 : if( FD_UNLIKELY( -1==fd_bootinfo_path_read( path, &info ) ) ) return; /* no or unreadable descriptor, older validator */
521 0 : if( FD_UNLIKELY( !fd_bootinfo_live( &info ) ) ) return;
522 0 : if( FD_UNLIKELY( !info.topo_layout_hash ) ) return;
523 :
524 0 : if( FD_UNLIKELY( info.topo_layout_hash!=config->topo.layout_hash ) ) {
525 0 : if( FD_UNLIKELY( !strcmp( info.commit_ref, fd_commit_ref_cstr ) ) ) {
526 0 : FD_LOG_ERR(( "The configuration file provided to this command is not the one the "
527 0 : "running validator was started with. Rerun the command with the "
528 0 : "validator's configuration file, or without --config to use the "
529 0 : "running validator's configuration automatically." ));
530 0 : }
531 0 : FD_LOG_ERR(( "This binary is a different version than the running validator. The "
532 0 : "validator is running commit %s%.11s%s and this binary is commit "
533 0 : "%s%.11s%s. Rerun the command with the same binary the validator was "
534 0 : "started with.",
535 0 : fd_log_style_bold(), info.commit_ref, fd_log_style_normal(),
536 0 : fd_log_style_bold(), fd_commit_ref_cstr, fd_log_style_normal() ));
537 0 : }
538 0 : }
|