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. */
126 0 : char blob_path[ PATH_MAX ];
127 0 : FD_TEST( fd_cstr_printf_check( blob_path, sizeof(blob_path), NULL, "%s/%s.config", config->hugetlbfs.mount_path, config->name ) );
128 0 : if( FD_UNLIKELY( -1==write_file_atomic( blob_path, config, sizeof(config_t), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ) ) ) {
129 0 : FD_LOG_WARNING(( "write(%s) failed (%i-%s), attaching to this validator will require --config", blob_path, errno, fd_io_strerror( errno ) ));
130 0 : } else {
131 0 : FD_TEST( fd_cstr_printf_check( info.config_file, sizeof(info.config_file), NULL, "%s.config", config->name ) );
132 0 : info.config_sz = sizeof(config_t);
133 0 : }
134 :
135 0 : char path[ PATH_MAX ];
136 0 : bootinfo_path( config->hugetlbfs.mount_path, config->name, path );
137 :
138 0 : if( FD_UNLIKELY( -1==write_file_atomic( path, &info, sizeof(info), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ) ) )
139 0 : FD_LOG_WARNING(( "write(%s) failed (%i-%s), validator discovery will be unavailable", path, errno, fd_io_strerror( errno ) ));
140 :
141 0 : char const * c_normal = fd_log_style_normal();
142 0 : char const * c_bold = fd_log_style_bold();
143 0 : char const * c_dim = fd_log_style_dim();
144 :
145 0 : ulong mem_gib = (fd_topo_mlock( &config->topo )+((1UL<<30UL)-1UL))>>30UL;
146 0 : FD_LOG_NOTICE(( "booting validator %s%s%s version %lu.%lu.%lu %s(%.11s)%s pid %lu with %s%lu%s tiles and %s%lu GiB%s memory at %s%s%s",
147 0 : c_bold, info.name, c_normal,
148 0 : info.fd_version[ 0 ], info.fd_version[ 1 ], info.fd_version[ 2 ],
149 0 : c_dim, info.commit_ref, c_normal,
150 0 : info.pid,
151 0 : c_bold, config->topo.tile_cnt, c_normal,
152 0 : c_bold, mem_gib, c_normal,
153 0 : c_dim, config->hugetlbfs.mount_path, c_normal ));
154 0 : }
155 :
156 : void
157 0 : fd_bootinfo_unlink( config_t const * config ) {
158 0 : char const * suffix[ 4 ] = { "bootinfo", "bootinfo.partial", "config", "config.partial" };
159 0 : for( ulong i=0UL; i<4UL; i++ ) {
160 0 : char path[ PATH_MAX ];
161 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s.%s", config->hugetlbfs.mount_path, config->name, suffix[ i ] ) );
162 0 : if( FD_UNLIKELY( -1==unlink( path ) && errno!=ENOENT ) )
163 0 : FD_LOG_WARNING(( "unlink(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
164 0 : }
165 0 : }
166 :
167 : int
168 : fd_bootinfo_path_read( char const * path,
169 0 : fd_bootinfo_t * out ) {
170 0 : int fd = open( path, O_RDONLY );
171 0 : if( FD_UNLIKELY( -1==fd ) ) return -1;
172 :
173 0 : struct stat st;
174 0 : if( FD_UNLIKELY( -1==fstat( fd, &st ) ) ) goto fail;
175 0 : if( FD_UNLIKELY( !S_ISREG( st.st_mode ) ) ) goto fail;
176 0 : if( FD_UNLIKELY( st.st_uid && st.st_uid!=geteuid() ) ) goto fail; /* only trust root or self */
177 0 : if( FD_UNLIKELY( st.st_mode & S_IWOTH ) ) goto fail;
178 : /* Newer builds may append fields, the v1 prefix is frozen so any
179 : genuine descriptor is at least this large and parses below. */
180 0 : if( FD_UNLIKELY( st.st_size<(long)sizeof(*out) ) ) goto fail;
181 :
182 0 : if( FD_UNLIKELY( sizeof(*out)!=(ulong)read( fd, out, sizeof(*out) ) ) ) goto fail;
183 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
184 :
185 0 : if( FD_UNLIKELY( out->magic!=FD_BOOTINFO_MAGIC ) ) return -1;
186 0 : if( FD_UNLIKELY( !out->version ) ) return -1;
187 0 : if( FD_UNLIKELY( !memchr( out->name, '\0', sizeof(out->name) ) ) ) return -1;
188 0 : if( FD_UNLIKELY( !memchr( out->commit_ref, '\0', sizeof(out->commit_ref) ) ) ) return -1;
189 0 : if( FD_UNLIKELY( !memchr( out->adminctl_wksp_file, '\0', sizeof(out->adminctl_wksp_file) ) ) ) return -1;
190 0 : if( FD_UNLIKELY( strchr( out->adminctl_wksp_file, '/' ) ) ) return -1;
191 0 : if( FD_UNLIKELY( !memchr( out->config_file, '\0', sizeof(out->config_file) ) ) ) return -1;
192 0 : if( FD_UNLIKELY( strchr( out->config_file, '/' ) ) ) return -1;
193 0 : return 0;
194 :
195 0 : fail:
196 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
197 0 : return -1;
198 0 : }
199 :
200 : int
201 0 : fd_bootinfo_live( fd_bootinfo_t const * info ) {
202 0 : ulong start_time = proc_start_time( info->pid );
203 0 : return start_time && start_time==info->pid_start_time;
204 0 : }
205 :
206 : /* mountinfo_unescape decodes \0NN octal escapes in-place. */
207 :
208 : static void
209 0 : mountinfo_unescape( char * s ) {
210 0 : char * w = s;
211 0 : while( *s ) {
212 0 : if( FD_UNLIKELY( s[0]=='\\' && s[1]>='0' && s[1]<='3' && s[2]>='0' && s[2]<='7' && s[3]>='0' && s[3]<='7' ) ) {
213 0 : *w++ = (char)( ((s[1]-'0')<<6) | ((s[2]-'0')<<3) | (s[3]-'0') );
214 0 : s += 4;
215 0 : } else {
216 0 : *w++ = *s++;
217 0 : }
218 0 : }
219 0 : *w = '\0';
220 0 : }
221 :
222 : /* candidate_mounts fills dirs with parent directories of firedancer
223 : shaped hugetlbfs mounts (mountpoint basename .gigantic/.huge/.normal)
224 : plus the default mount path. Returns the count. */
225 :
226 : static ulong
227 : candidate_mounts( char dirs[ FD_BOOTINFO_INSTANCE_MAX ][ PATH_MAX ],
228 0 : ulong max ) {
229 0 : ulong cnt = 0UL;
230 :
231 0 : FILE * fp = fopen( "/proc/self/mountinfo", "r" );
232 0 : if( FD_LIKELY( fp ) ) {
233 0 : char line[ 4096UL ];
234 0 : while( FD_LIKELY( fgets( line, sizeof(line), fp ) ) ) {
235 : /* fields: id parent maj:min root mountpoint opts [optional]* - fstype src superopts */
236 0 : char * sep = strstr( line, " - " );
237 0 : if( FD_UNLIKELY( !sep ) ) continue;
238 :
239 0 : char * fstype = sep+3UL;
240 0 : char * fstype_end = strchr( fstype, ' ' );
241 0 : if( FD_UNLIKELY( !fstype_end ) ) continue;
242 0 : *fstype_end = '\0';
243 0 : if( FD_LIKELY( strcmp( fstype, "hugetlbfs" ) ) ) continue;
244 :
245 0 : *sep = '\0';
246 0 : char * mountpoint = NULL;
247 0 : char * tok = strtok( line, " " );
248 0 : for( ulong i=0UL; tok; i++, tok=strtok( NULL, " " ) ) {
249 0 : if( i==4UL ) { mountpoint = tok; break; }
250 0 : }
251 0 : if( FD_UNLIKELY( !mountpoint ) ) continue;
252 0 : mountinfo_unescape( mountpoint );
253 :
254 0 : char * base = strrchr( mountpoint, '/' );
255 0 : if( FD_UNLIKELY( !base || base==mountpoint ) ) continue;
256 0 : if( FD_LIKELY( strcmp( base, "/.gigantic" ) && strcmp( base, "/.huge" ) && strcmp( base, "/.normal" ) ) ) continue;
257 0 : *base = '\0'; /* mountpoint is now the parent dir */
258 :
259 0 : int seen = 0;
260 0 : for( ulong i=0UL; i<cnt; i++ ) if( !strcmp( dirs[ i ], mountpoint ) ) { seen = 1; break; }
261 0 : if( FD_UNLIKELY( seen ) ) continue;
262 0 : if( FD_UNLIKELY( cnt>=max ) ) break;
263 0 : strncpy( dirs[ cnt ], mountpoint, PATH_MAX-1UL );
264 0 : dirs[ cnt ][ PATH_MAX-1UL ] = '\0';
265 0 : cnt++;
266 0 : }
267 0 : if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_WARNING(( "fclose() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
268 0 : }
269 :
270 : /* default path, covers normal page setups with no hugetlbfs mounts */
271 0 : int seen = 0;
272 0 : for( ulong i=0UL; i<cnt; i++ ) if( !strcmp( dirs[ i ], "/mnt/.fd" ) ) { seen = 1; break; }
273 0 : if( FD_LIKELY( !seen && cnt<max ) ) {
274 0 : strncpy( dirs[ cnt ], "/mnt/.fd", PATH_MAX-1UL );
275 0 : cnt++;
276 0 : }
277 :
278 0 : return cnt;
279 0 : }
280 :
281 : ulong
282 : fd_bootinfo_discover( fd_bootinfo_instance_t * out,
283 0 : ulong max ) {
284 0 : static char dirs[ FD_BOOTINFO_INSTANCE_MAX ][ PATH_MAX ];
285 0 : ulong dir_cnt = candidate_mounts( dirs, FD_BOOTINFO_INSTANCE_MAX );
286 :
287 0 : ulong cnt = 0UL;
288 0 : for( ulong i=0UL; i<dir_cnt && cnt<max; i++ ) {
289 0 : DIR * dir = opendir( dirs[ i ] );
290 0 : if( FD_UNLIKELY( !dir ) ) continue;
291 :
292 0 : struct dirent * entry;
293 0 : while( FD_LIKELY( cnt<max && (entry=readdir( dir )) ) ) {
294 0 : char const * suffix = strstr( entry->d_name, ".bootinfo" );
295 0 : if( FD_LIKELY( !suffix || strcmp( suffix, ".bootinfo" ) ) ) continue;
296 :
297 0 : char path[ PATH_MAX ];
298 0 : if( FD_UNLIKELY( !fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s", dirs[ i ], entry->d_name ) ) ) continue;
299 :
300 0 : fd_bootinfo_t info;
301 0 : if( FD_UNLIKELY( -1==fd_bootinfo_path_read( path, &info ) ) ) continue;
302 :
303 : /* name must match filename */
304 0 : if( FD_UNLIKELY( strncmp( entry->d_name, info.name, (ulong)(suffix-entry->d_name) ) || strlen( info.name )!=(ulong)(suffix-entry->d_name) ) ) continue;
305 :
306 0 : strncpy( out[ cnt ].mount_path, dirs[ i ], PATH_MAX-1UL );
307 0 : out[ cnt ].mount_path[ PATH_MAX-1UL ] = '\0';
308 0 : out[ cnt ].info = info;
309 0 : out[ cnt ].live = fd_bootinfo_live( &info );
310 0 : cnt++;
311 0 : }
312 0 : if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_WARNING(( "closedir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
313 0 : }
314 :
315 0 : return cnt;
316 0 : }
317 :
318 : void
319 : fd_bootinfo_print( fd_bootinfo_instance_t const * instances,
320 0 : ulong cnt ) {
321 : /* fd_log colorize already honors NO_COLOR/TERM/--log-colorize, but is
322 : keyed to stderr, so additionally require stdout to be a tty. */
323 0 : int color = fd_log_colorize() && isatty( STDOUT_FILENO );
324 0 : char const * c_normal = color ? BOOTINFO_NORMAL : "";
325 0 : char const * c_bold = color ? BOOTINFO_BOLD : "";
326 0 : char const * c_dim = color ? BOOTINFO_DIM : "";
327 0 : char const * c_live = color ? BOOTINFO_GREEN : "";
328 0 : char const * c_stale = color ? BOOTINFO_YELLOW : "";
329 :
330 0 : if( FD_UNLIKELY( !cnt ) ) {
331 0 : FD_LOG_STDOUT(( "%sno validators found%s\n", c_dim, c_normal ));
332 0 : return;
333 0 : }
334 :
335 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 ));
336 0 : for( ulong i=0UL; i<cnt; i++ ) {
337 0 : fd_bootinfo_instance_t const * instance = &instances[ i ];
338 0 : fd_bootinfo_t const * info = &instance->info;
339 :
340 0 : char uptime[ 32UL ] = "-";
341 0 : if( FD_LIKELY( instance->live ) ) {
342 0 : long secs = (fd_log_wallclock()-info->boot_wallclock_nanos)/(long)1e9;
343 0 : if( FD_UNLIKELY( secs<0L ) ) secs = 0L;
344 0 : long days = secs/86400L, hours = (secs/3600L)%24L, mins = (secs/60L)%60L;
345 0 : if( FD_UNLIKELY( days ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldd%ldh%ldm", days, hours, mins ) );
346 0 : else if( FD_UNLIKELY( hours ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldh%ldm", hours, mins ) );
347 0 : else if( FD_LIKELY( mins ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldm", mins ) );
348 0 : else FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%lds", secs ) );
349 0 : }
350 :
351 0 : char version[ 32UL ];
352 0 : FD_TEST( fd_cstr_printf_check( version, sizeof(version), NULL, "%lu.%lu.%lu", info->fd_version[ 0 ], info->fd_version[ 1 ], info->fd_version[ 2 ] ) );
353 :
354 0 : char commit[ 12UL ] = {0};
355 0 : strncpy( commit, info->commit_ref, sizeof(commit)-1UL );
356 :
357 0 : FD_LOG_STDOUT(( "%s%-16s%s %-8lu %s%-7s%s %-10s %-12s %-12s %s%s%s\n",
358 0 : c_bold, info->name, c_normal,
359 0 : info->pid,
360 0 : instance->live ? c_live : c_stale,
361 0 : instance->live ? "live" : "stale",
362 0 : c_normal,
363 0 : uptime,
364 0 : version,
365 0 : commit,
366 0 : c_dim, instance->mount_path, c_normal ));
367 0 : }
368 0 : }
369 :
370 : void
371 0 : fd_bootinfo_notice( fd_bootinfo_instance_t const * instance ) {
372 0 : fd_bootinfo_t const * info = &instance->info;
373 :
374 0 : long secs = (fd_log_wallclock()-info->boot_wallclock_nanos)/(long)1e9;
375 0 : if( FD_UNLIKELY( secs<0L ) ) secs = 0L;
376 0 : long days = secs/86400L, hours = (secs/3600L)%24L, mins = (secs/60L)%60L;
377 0 : char uptime[ 32UL ];
378 0 : if( FD_UNLIKELY( days ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldd%ldh%ldm", days, hours, mins ) );
379 0 : else if( FD_UNLIKELY( hours ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldh%ldm", hours, mins ) );
380 0 : else if( FD_LIKELY( mins ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldm", mins ) );
381 0 : else FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%lds", secs ) );
382 :
383 0 : char const * c_normal = fd_log_style_normal();
384 0 : char const * c_bold = fd_log_style_bold();
385 0 : char const * c_dim = fd_log_style_dim();
386 0 : char const * c_live = fd_log_colorize() ? BOOTINFO_GREEN : "";
387 :
388 0 : FD_LOG_NOTICE(( "attached to %slive%s validator %s%s%s version %lu.%lu.%lu %s(%.11s)%s pid %lu up %s at %s%s%s",
389 0 : c_live, c_normal,
390 0 : c_bold, info->name, c_normal,
391 0 : info->fd_version[ 0 ], info->fd_version[ 1 ], info->fd_version[ 2 ],
392 0 : c_dim, info->commit_ref, c_normal,
393 0 : info->pid,
394 0 : uptime,
395 0 : c_dim, instance->mount_path, c_normal ));
396 0 : }
397 :
398 : void
399 0 : fd_bootinfo_adopt( config_t * config ) {
400 0 : if( FD_LIKELY( config->has_user_config ) ) return; /* fd_bootinfo_check_layout verifies before joining */
401 :
402 0 : fd_bootinfo_instance_t instances[ FD_BOOTINFO_INSTANCE_MAX ];
403 0 : ulong cnt = fd_bootinfo_discover( instances, FD_BOOTINFO_INSTANCE_MAX );
404 :
405 0 : fd_bootinfo_instance_t * match = NULL;
406 0 : ulong live_cnt = 0UL;
407 0 : for( ulong i=0UL; i<cnt; i++ ) {
408 0 : if( FD_UNLIKELY( !instances[ i ].live ) ) continue;
409 0 : match = &instances[ i ];
410 0 : live_cnt++;
411 0 : }
412 :
413 0 : if( FD_UNLIKELY( !live_cnt ) ) {
414 0 : if( FD_UNLIKELY( cnt ) ) fd_bootinfo_print( instances, cnt );
415 0 : FD_LOG_ERR(( "No running validator found. If a validator is running, pass --config with the "
416 0 : "configuration file it was started with." ));
417 0 : }
418 :
419 0 : if( FD_UNLIKELY( live_cnt>1UL ) ) {
420 0 : fd_bootinfo_print( instances, cnt );
421 0 : FD_LOG_ERR(( "Multiple running validators found. Pass --config to select one." ));
422 0 : }
423 :
424 0 : fd_bootinfo_t const * info = &match->info;
425 :
426 0 : if( FD_UNLIKELY( strcmp( info->commit_ref, fd_commit_ref_cstr ) ) )
427 0 : FD_LOG_ERR(( "The running validator `%s` is running commit %s but this binary is commit %s. "
428 0 : "This command reads the validator's memory directly and must be run from the "
429 0 : "same binary the validator is running.", info->name, info->commit_ref, fd_commit_ref_cstr ));
430 :
431 0 : if( FD_UNLIKELY( !info->config_file[ 0 ] || info->config_sz!=sizeof(config_t) ) )
432 0 : FD_LOG_ERR(( "The running validator `%s` did not publish a usable configuration. Pass --config "
433 0 : "with the configuration file it was started with.", info->name ));
434 :
435 0 : char path[ PATH_MAX ];
436 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s", match->mount_path, info->config_file ) );
437 :
438 0 : int fd = open( path, O_RDONLY );
439 0 : if( FD_UNLIKELY( -1==fd ) ) FD_LOG_ERR(( "open(%s) failed (%i-%s). The validator `%s` appears to be running "
440 0 : "but its configuration could not be read. Pass --config with the "
441 0 : "configuration file it was started with.", path, errno, fd_io_strerror( errno ), info->name ));
442 :
443 0 : struct stat st;
444 0 : if( FD_UNLIKELY( -1==fstat( fd, &st ) ) ) FD_LOG_ERR(( "fstat(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
445 0 : if( FD_UNLIKELY( !S_ISREG( st.st_mode ) ) ) FD_LOG_ERR(( "`%s` is not a regular file", path ));
446 0 : if( FD_UNLIKELY( st.st_uid && st.st_uid!=geteuid() ) ) FD_LOG_ERR(( "`%s` is not owned by root", path ));
447 0 : if( FD_UNLIKELY( st.st_mode & S_IWOTH ) ) FD_LOG_ERR(( "`%s` is world writable", path ));
448 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 ));
449 :
450 0 : uchar * p = (uchar *)config;
451 0 : ulong remaining = sizeof(config_t);
452 0 : while( remaining ) {
453 0 : long got = read( fd, p, remaining );
454 0 : if( FD_UNLIKELY( got<=0L ) ) {
455 0 : if( FD_UNLIKELY( got<0L && errno==EINTR ) ) continue;
456 0 : FD_LOG_ERR(( "read(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
457 0 : }
458 0 : p += got; remaining -= (ulong)got;
459 0 : }
460 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
461 :
462 0 : config->has_user_config = 0;
463 :
464 : /* Rebind process state that was derived from the default config
465 : before adoption. */
466 0 : fd_tempo_set_tick_per_ns( config->tick_per_ns_mu, config->tick_per_ns_sigma );
467 :
468 0 : ulong base_len = strlen( config->hugetlbfs.mount_path );
469 0 : if( FD_UNLIKELY( !base_len || base_len>=FD_SHMEM_PRIVATE_BASE_MAX ) ) FD_LOG_ERR(( "adopted invalid mount path" ));
470 0 : memcpy( fd_shmem_private_base, config->hugetlbfs.mount_path, base_len+1UL );
471 0 : fd_shmem_private_base_len = base_len;
472 :
473 : /* Permissions were checked against the default topology, which may
474 : mlock less than the adopted one. Best effort raise. */
475 0 : struct rlimit rl = { .rlim_cur = fd_topo_mlock( &config->topo ), .rlim_max = fd_topo_mlock( &config->topo ) };
476 0 : if( FD_UNLIKELY( -1==setrlimit( RLIMIT_MEMLOCK, &rl ) ) )
477 0 : FD_LOG_INFO(( "setrlimit(RLIMIT_MEMLOCK) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
478 :
479 0 : fd_bootinfo_notice( match );
480 0 : }
481 :
482 : void
483 0 : fd_bootinfo_check_layout( config_t const * config ) {
484 0 : char path[ PATH_MAX ];
485 0 : bootinfo_path( config->hugetlbfs.mount_path, config->name, path );
486 :
487 0 : fd_bootinfo_t info;
488 0 : if( FD_UNLIKELY( -1==fd_bootinfo_path_read( path, &info ) ) ) return; /* no or unreadable descriptor, older validator */
489 0 : if( FD_UNLIKELY( !fd_bootinfo_live( &info ) ) ) return;
490 0 : if( FD_UNLIKELY( !info.topo_layout_hash ) ) return;
491 :
492 0 : if( FD_UNLIKELY( info.topo_layout_hash!=config->topo.layout_hash ) ) {
493 0 : if( FD_UNLIKELY( !strcmp( info.commit_ref, fd_commit_ref_cstr ) ) ) {
494 0 : FD_LOG_ERR(( "The configuration file provided to this command is not the one the "
495 0 : "running validator was started with. Rerun the command with the "
496 0 : "validator's configuration file, or without --config to use the "
497 0 : "running validator's configuration automatically." ));
498 0 : }
499 0 : FD_LOG_ERR(( "This binary is a different version than the running validator. The "
500 0 : "validator is running commit %s%.11s%s and this binary is commit "
501 0 : "%s%.11s%s. Rerun the command with the same binary the validator was "
502 0 : "started with.",
503 0 : fd_log_style_bold(), info.commit_ref, fd_log_style_normal(),
504 0 : fd_log_style_bold(), fd_commit_ref_cstr, fd_log_style_normal() ));
505 0 : }
506 0 : }
|