File Index
Files and directories
Sign in
FILE

in_kernel.c

File preview with syntax highlighting.

linux-6.19.14/tools/verification/rv/src/in_kernel.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * in kernel monitor support: allows rv to control in-kernel monitors.
  4 *
  5 * Copyright (C) 2022 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
  6 */
  7#include <getopt.h>
  8#include <stdlib.h>
  9#include <stdio.h>
 10#include <string.h>
 11#include <errno.h>
 12#include <unistd.h>
 13#include <dirent.h>
 14
 15#include <trace.h>
 16#include <utils.h>
 17#include <rv.h>
 18
 19static int config_has_id;
 20static int config_is_container;
 21static int config_my_pid;
 22static int config_trace;
 23
 24static char *config_initial_reactor;
 25static char *config_reactor;
 26
 27/*
 28 * __ikm_read_enable - reads monitor's enable status
 29 *
 30 * __does not log errors.
 31 *
 32 * Returns the current status, or -1 if the monitor does not exist,
 33 * __hence not logging errors.
 34 */
 35static int __ikm_read_enable(char *monitor_name)
 36{
 37	char path[MAX_PATH];
 38	long long enabled;
 39	int retval;
 40
 41	snprintf(path, MAX_PATH, "rv/monitors/%s/enable", monitor_name);
 42
 43	retval = tracefs_instance_file_read_number(NULL, path, &enabled);
 44	if (retval < 0)
 45		return -1;
 46
 47	return enabled;
 48}
 49
 50/*
 51 * __ikm_find_monitor - find the full name of a possibly nested module
 52 *
 53 * __does not log errors.
 54 *
 55 * Returns 1 if we found the monitor, -1 on error and 0 if it does not exist.
 56 * The string out_name is populated with the full name, which can be
 57 * equal to monitor_name or container/monitor_name if nested
 58 */
 59static int __ikm_find_monitor_name(char *monitor_name, char *out_name)
 60{
 61	char *available_monitors, container[MAX_DA_NAME_LEN+1], *cursor, *end;
 62	int retval = 1;
 63
 64	available_monitors = tracefs_instance_file_read(NULL, "rv/available_monitors", NULL);
 65	if (!available_monitors)
 66		return -1;
 67
 68	cursor = strstr(available_monitors, monitor_name);
 69	if (!cursor) {
 70		retval = 0;
 71		goto out_free;
 72	}
 73
 74	for (; cursor > available_monitors; cursor--)
 75		if (*(cursor-1) == '\n')
 76			break;
 77	end = strstr(cursor, "\n");
 78	memcpy(out_name, cursor, end-cursor);
 79	out_name[end-cursor] = '\0';
 80
 81	cursor = strstr(out_name, ":");
 82	if (cursor)
 83		*cursor = '/';
 84	else {
 85		sprintf(container, "%s:", monitor_name);
 86		if (strstr(available_monitors, container))
 87			config_is_container = 1;
 88	}
 89
 90out_free:
 91	free(available_monitors);
 92	return retval;
 93}
 94
 95/*
 96 * ikm_read_enable - reads monitor's enable status
 97 *
 98 * Returns the current status, or -1 on error.
 99 */
100static int ikm_read_enable(char *monitor_name)
101{
102	int enabled;
103
104	enabled = __ikm_read_enable(monitor_name);
105	if (enabled < 0) {
106		err_msg("ikm: fail read enabled: %d\n", enabled);
107		return -1;
108	}
109
110	debug_msg("ikm: read enabled: %d\n", enabled);
111
112	return enabled;
113}
114
115/*
116 * ikm_write_enable - write to the monitor's enable file
117 *
118 * Return the number of bytes written, -1 on error.
119 */
120static int ikm_write_enable(char *monitor_name, char *enable_disable)
121{
122	char path[MAX_PATH];
123	int retval;
124
125	debug_msg("ikm: writing enabled: %s\n", enable_disable);
126
127	snprintf(path, MAX_PATH, "rv/monitors/%s/enable", monitor_name);
128	retval = tracefs_instance_file_write(NULL, path, enable_disable);
129	if (retval < strlen(enable_disable)) {
130		err_msg("ikm: writing enabled: %s\n", enable_disable);
131		return -1;
132	}
133
134	return retval;
135}
136
137/*
138 * ikm_enable - enable a monitor
139 *
140 * Returns -1 on failure. Success otherwise.
141 */
142static int ikm_enable(char *monitor_name)
143{
144	return ikm_write_enable(monitor_name, "1");
145}
146
147/*
148 * ikm_disable - disable a monitor
149 *
150 * Returns -1 on failure. Success otherwise.
151 */
152static int ikm_disable(char *monitor_name)
153{
154	return ikm_write_enable(monitor_name, "0");
155}
156
157/*
158 * ikm_read_desc - read monitors' description
159 *
160 * Return a dynamically allocated string with the monitor's
161 * description, NULL otherwise.
162 */
163static char *ikm_read_desc(char *monitor_name)
164{
165	char path[MAX_PATH];
166	char *desc;
167
168	snprintf(path, MAX_PATH, "rv/monitors/%s/desc", monitor_name);
169	desc = tracefs_instance_file_read(NULL, path, NULL);
170	if (!desc) {
171		err_msg("ikm: error reading monitor %s desc\n", monitor_name);
172		return NULL;
173	}
174
175	*strstr(desc, "\n") = '\0';
176
177	return desc;
178}
179
180/*
181 * ikm_fill_monitor_definition - fill monitor's definition
182 *
183 * Returns -1 on error, 1 if the monitor does not belong in the container, 0 otherwise.
184 * container can be NULL
185 */
186static int ikm_fill_monitor_definition(char *name, struct monitor *ikm, char *container)
187{
188	int enabled;
189	char *desc, *nested_name;
190
191	nested_name = strstr(name, ":");
192	if (nested_name) {
193		/* it belongs in container if it starts with "container:" */
194		if (container && strstr(name, container) != name)
195			return 1;
196		*nested_name = '/';
197		++nested_name;
198		ikm->nested = 1;
199	} else {
200		if (container)
201			return 1;
202		nested_name = name;
203		ikm->nested = 0;
204	}
205
206	enabled = ikm_read_enable(name);
207	if (enabled < 0) {
208		err_msg("ikm: monitor %s fail to read enable file, bug?\n", name);
209		return -1;
210	}
211
212	desc = ikm_read_desc(name);
213	if (!desc) {
214		err_msg("ikm: monitor %s does not have desc file, bug?\n", name);
215		return -1;
216	}
217
218	strncpy(ikm->name, nested_name, MAX_DA_NAME_LEN);
219	ikm->enabled = enabled;
220	strncpy(ikm->desc, desc, MAX_DESCRIPTION);
221
222	free(desc);
223
224	return 0;
225}
226
227/*
228 * ikm_write_reactor - switch the reactor to *reactor
229 *
230 * Return the number or characters written, -1 on error.
231 */
232static int ikm_write_reactor(char *monitor_name, char *reactor)
233{
234	char path[MAX_PATH];
235	int retval;
236
237	snprintf(path, MAX_PATH, "rv/monitors/%s/reactors", monitor_name);
238	retval = tracefs_instance_file_write(NULL, path, reactor);
239	debug_msg("ikm: write \"%s\" reactors: %d\n", reactor, retval);
240
241	return retval;
242}
243
244/*
245 * ikm_read_reactor - read the reactors file
246 *
247 * Returns a dynamically allocated string with monitor's
248 * available reactors, or NULL on error.
249 */
250static char *ikm_read_reactor(char *monitor_name)
251{
252	char path[MAX_PATH];
253	char *reactors;
254
255	snprintf(path, MAX_PATH, "rv/monitors/%s/reactors", monitor_name);
256	reactors = tracefs_instance_file_read(NULL, path, NULL);
257	if (!reactors) {
258		err_msg("ikm: fail reading monitor's %s reactors file\n", monitor_name);
259		return NULL;
260	}
261
262	return reactors;
263}
264
265/*
266 * ikm_get_current_reactor - get the current enabled reactor
267 *
268 * Reads the reactors file and find the currently enabled
269 * [reactor].
270 *
271 * Returns a dynamically allocated memory with the current
272 * reactor. NULL otherwise.
273 */
274static char *ikm_get_current_reactor(char *monitor_name)
275{
276	char *reactors = ikm_read_reactor(monitor_name);
277	char *curr_reactor = NULL;
278	char *start;
279	char *end;
280
281	if (!reactors)
282		return NULL;
283
284	start = strstr(reactors, "[");
285	if (!start)
286		goto out_free;
287
288	start++;
289
290	end = strstr(start, "]");
291	if (!end)
292		goto out_free;
293
294	*end = '\0';
295
296	curr_reactor = calloc(strlen(start) + 1, sizeof(char));
297	if (!curr_reactor)
298		goto out_free;
299
300	strncpy(curr_reactor, start, strlen(start));
301	debug_msg("ikm: read current reactor %s\n", curr_reactor);
302
303out_free:
304	free(reactors);
305
306	return curr_reactor;
307}
308
309static int ikm_has_id(char *monitor_name)
310{
311	char path[MAX_PATH];
312	char *format;
313	int has_id;
314
315	snprintf(path, MAX_PATH, "events/rv/event_%s/format", monitor_name);
316	format = tracefs_instance_file_read(NULL, path, NULL);
317	if (!format) {
318		err_msg("ikm: fail reading monitor's %s format event file\n", monitor_name);
319		return -1;
320	}
321
322	/* print fmt: "%d: %s x %s -> %s %s", REC->id, ... */
323	has_id = !!strstr(format, "REC->id");
324
325	debug_msg("ikm: monitor %s has id: %s\n", monitor_name, has_id ? "yes" : "no");
326
327	free(format);
328
329	return has_id;
330}
331
332/**
333 * ikm_list_monitors - list all available monitors
334 *
335 * Returns 0 on success, -1 otherwise.
336 */
337int ikm_list_monitors(char *container)
338{
339	char *available_monitors;
340	struct monitor ikm = {0};
341	char *curr, *next;
342	int retval, list_monitor = 0;
343
344	available_monitors = tracefs_instance_file_read(NULL, "rv/available_monitors", NULL);
345
346	if (!available_monitors) {
347		err_msg("ikm: available monitors is not available, is CONFIG_RV enabled?\n");
348		return -1;
349	}
350
351	curr = available_monitors;
352	do {
353		next = strstr(curr, "\n");
354		*next = '\0';
355
356		retval = ikm_fill_monitor_definition(curr, &ikm, container);
357		if (retval < 0)
358			err_msg("ikm: error reading %d in kernel monitor, skipping\n", curr);
359
360		if (!retval) {
361			int indent = ikm.nested && !container;
362
363			list_monitor = 1;
364			printf("%s%-*s %s %s\n", indent ? " - " : "",
365			       indent ? MAX_DA_NAME_LEN - 3 : MAX_DA_NAME_LEN,
366			       ikm.name, ikm.desc, ikm.enabled ? "[ON]" : "[OFF]");
367		}
368		curr = ++next;
369
370	} while (strlen(curr));
371
372	if (!list_monitor) {
373		if (container)
374			printf("-- No monitor found in container %s --\n", container);
375		else
376			printf("-- No monitor found --\n");
377	}
378
379	free(available_monitors);
380
381	return 0;
382}
383
384static void ikm_print_header(struct trace_seq *s)
385{
386	trace_seq_printf(s, "%16s-%-8s %5s %5s ", "<TASK>", "PID", "[CPU]", "TYPE");
387	if (config_has_id)
388		trace_seq_printf(s, "%8s ", "ID");
389
390	trace_seq_printf(s, "%24s x %-24s -> %-24s %s\n",
391			 "STATE",
392			 "EVENT",
393			 "NEXT_STATE",
394			 "FINAL");
395
396	trace_seq_printf(s, "%16s %-8s %5s %5s ", " | ", " | ", " | ", " | ");
397
398	if (config_has_id)
399		trace_seq_printf(s, "%8s ", " | ");
400
401	trace_seq_printf(s, "%24s   %-24s    %-24s %s\n",
402			 " | ",
403			 " | ",
404			 " | ",
405			 "|");
406
407}
408
409/*
410 * ikm_event_handler - callback to handle event events
411 *
412 * Called any time a rv:"monitor"_event events is generated.
413 * It parses and prints event.
414 */
415static int
416ikm_event_handler(struct trace_seq *s, struct tep_record *record,
417		  struct tep_event *trace_event, void *context)
418{
419	/* if needed: struct trace_instance *inst = context; */
420	char *state, *event, *next_state;
421	unsigned long long final_state;
422	unsigned long long pid;
423	unsigned long long id;
424	int val;
425	bool missing_id;
426
427	if (config_has_id)
428		missing_id = tep_get_field_val(s, trace_event, "id", record, &id, 1);
429
430	tep_get_common_field_val(s, trace_event, "common_pid", record, &pid, 1);
431
432	if (config_has_id && (config_my_pid == id))
433		return 0;
434	else if (config_my_pid == pid)
435		return 0;
436
437	tep_print_event(trace_event->tep, s, record, "%16s-%-8d [%.3d] ",
438			TEP_PRINT_COMM, TEP_PRINT_PID, TEP_PRINT_CPU);
439
440	if (config_is_container)
441		tep_print_event(trace_event->tep, s, record, "%s ", TEP_PRINT_NAME);
442	else
443		trace_seq_printf(s, "event ");
444
445	if (config_has_id) {
446		if (missing_id)
447			/* placeholder if we are dealing with a mixed-type container*/
448			trace_seq_printf(s, "        ");
449		else
450			trace_seq_printf(s, "%8llu ", id);
451	}
452
453	state = tep_get_field_raw(s, trace_event, "state", record, &val, 0);
454	event = tep_get_field_raw(s, trace_event, "event", record, &val, 0);
455	next_state = tep_get_field_raw(s, trace_event, "next_state", record, &val, 0);
456	tep_get_field_val(s, trace_event, "final_state", record, &final_state, 1);
457
458	trace_seq_printf(s, "%24s x %-24s -> %-24s %s\n",
459			 state,
460			 event,
461			 next_state,
462			 final_state ? "Y" : "N");
463
464	trace_seq_do_printf(s);
465	trace_seq_reset(s);
466
467	return 0;
468}
469
470/*
471 * ikm_error_handler - callback to handle error events
472 *
473 * Called any time a rv:"monitor"_errors events is generated.
474 * It parses and prints event.
475 */
476static int
477ikm_error_handler(struct trace_seq *s, struct tep_record *record,
478		  struct tep_event *trace_event, void *context)
479{
480	unsigned long long pid, id;
481	int cpu = record->cpu;
482	char *state, *event;
483	int val;
484	bool missing_id;
485
486	if (config_has_id)
487		missing_id = tep_get_field_val(s, trace_event, "id", record, &id, 1);
488
489	tep_get_common_field_val(s, trace_event, "common_pid", record, &pid, 1);
490
491	if (config_has_id && config_my_pid == id)
492		return 0;
493	else if (config_my_pid == pid)
494		return 0;
495
496	trace_seq_printf(s, "%8lld [%03d] ", pid, cpu);
497
498	if (config_is_container)
499		tep_print_event(trace_event->tep, s, record, "%s ", TEP_PRINT_NAME);
500	else
501		trace_seq_printf(s, "error ");
502
503	if (config_has_id) {
504		if (missing_id)
505			/* placeholder if we are dealing with a mixed-type container*/
506			trace_seq_printf(s, "        ");
507		else
508			trace_seq_printf(s, "%8llu ", id);
509	}
510
511	state = tep_get_field_raw(s, trace_event, "state", record, &val, 0);
512	event = tep_get_field_raw(s, trace_event, "event", record, &val, 0);
513
514	trace_seq_printf(s, "%24s x %s\n", state, event);
515
516	trace_seq_do_printf(s);
517	trace_seq_reset(s);
518
519	return 0;
520}
521
522static int ikm_enable_trace_events(char *monitor_name, struct trace_instance *inst)
523{
524	char event[MAX_DA_NAME_LEN + 7]; /* max(error_,event_) + '0' = 7 */
525	int retval;
526
527	snprintf(event, sizeof(event), "event_%s", monitor_name);
528	retval = tracefs_event_enable(inst->inst, "rv",  event);
529	if (retval)
530		return -1;
531
532	tep_register_event_handler(inst->tep, -1, "rv", event,
533				   ikm_event_handler, NULL);
534
535	snprintf(event, sizeof(event), "error_%s", monitor_name);
536	retval = tracefs_event_enable(inst->inst, "rv", event);
537	if (retval)
538		return -1;
539
540	tep_register_event_handler(inst->tep, -1, "rv", event,
541				   ikm_error_handler, NULL);
542
543	/* set if at least 1 monitor has id in case of a container */
544	config_has_id = ikm_has_id(monitor_name);
545	if (config_has_id < 0)
546		return -1;
547
548
549	return 0;
550}
551
552static int ikm_enable_trace_container(char *monitor_name,
553				      struct trace_instance *inst)
554{
555	DIR *dp;
556	char *abs_path, rv_path[MAX_PATH];
557	struct dirent *ep;
558	int retval = 0;
559
560	snprintf(rv_path, MAX_PATH, "rv/monitors/%s", monitor_name);
561	abs_path = tracefs_instance_get_file(NULL, rv_path);
562	if (!abs_path)
563		return -1;
564	dp = opendir(abs_path);
565	if (!dp)
566		goto out_free;
567
568	while (!retval && (ep = readdir(dp))) {
569		if (ep->d_type != DT_DIR || ep->d_name[0] == '.')
570			continue;
571		retval = ikm_enable_trace_events(ep->d_name, inst);
572	}
573
574	closedir(dp);
575out_free:
576	free(abs_path);
577	return retval;
578}
579
580/*
581 * ikm_setup_trace_instance - set up a tracing instance to collect data
582 *
583 * Create a trace instance, enable rv: events and enable the trace.
584 *
585 * Returns the trace_instance * with all set, NULL otherwise.
586 */
587static struct trace_instance *ikm_setup_trace_instance(char *monitor_name)
588{
589	struct trace_instance *inst;
590	int retval;
591
592	if (!config_trace)
593		return NULL;
594
595	/* alloc data */
596	inst = calloc(1, sizeof(*inst));
597	if (!inst) {
598		err_msg("ikm: failed to allocate trace instance");
599		goto out_err;
600	}
601
602	retval = trace_instance_init(inst, monitor_name);
603	if (retval)
604		goto out_free;
605
606	if (config_is_container)
607		retval = ikm_enable_trace_container(monitor_name, inst);
608	else
609		retval = ikm_enable_trace_events(monitor_name, inst);
610	if (retval)
611		goto out_inst;
612
613	/* ready to enable */
614	tracefs_trace_on(inst->inst);
615
616	return inst;
617
618out_inst:
619	trace_instance_destroy(inst);
620out_free:
621	free(inst);
622out_err:
623	return NULL;
624}
625
626/**
627 * ikm_destroy_trace_instance - destroy a previously created instance
628 */
629static void ikm_destroy_trace_instance(struct trace_instance *inst)
630{
631	if (!inst)
632		return;
633
634	trace_instance_destroy(inst);
635	free(inst);
636}
637
638/*
639 * ikm_usage_print_reactors - print all available reactors, one per line.
640 */
641static void ikm_usage_print_reactors(void)
642{
643	char *reactors = tracefs_instance_file_read(NULL, "rv/available_reactors", NULL);
644	char *start, *end;
645
646	if (!reactors)
647		return;
648
649	fprintf(stderr, "  available reactors:");
650
651	start = reactors;
652	end = strstr(start, "\n");
653
654	while (end) {
655		*end = '\0';
656
657		fprintf(stderr, " %s", start);
658
659		start = ++end;
660		end = strstr(start, "\n");
661	}
662
663	fprintf(stderr, "\n");
664}
665/*
666 * ikm_usage - print usage
667 */
668static void ikm_usage(int exit_val, char *monitor_name, const char *fmt, ...)
669{
670
671	char message[1024];
672	va_list ap;
673	int i;
674
675	static const char *const usage[] = {
676		"",
677		"	-h/--help: print this menu and the reactor list",
678		"	-r/--reactor 'reactor': enables the 'reactor'",
679		"	-s/--self: when tracing (-t), also trace rv command",
680		"	-t/--trace: trace monitor's event",
681		"	-v/--verbose: print debug messages",
682		"",
683		NULL,
684	};
685
686	va_start(ap, fmt);
687	vsnprintf(message, sizeof(message), fmt, ap);
688	va_end(ap);
689
690	fprintf(stderr, "  %s\n", message);
691
692	fprintf(stderr, "\n  usage: rv mon %s [-h] [-q] [-r reactor] [-s] [-v]", monitor_name);
693
694	for (i = 0; usage[i]; i++)
695		fprintf(stderr, "%s\n", usage[i]);
696
697	ikm_usage_print_reactors();
698	exit(exit_val);
699}
700
701/*
702 * parse_arguments - parse arguments and set config
703 */
704static int parse_arguments(char *monitor_name, int argc, char **argv)
705{
706	int c, retval;
707
708	config_my_pid = getpid();
709
710	while (1) {
711		static struct option long_options[] = {
712			{"help",		no_argument,		0, 'h'},
713			{"reactor",		required_argument,	0, 'r'},
714			{"self",		no_argument,		0, 's'},
715			{"trace",		no_argument,		0, 't'},
716			{"verbose",		no_argument,		0, 'v'},
717			{0, 0, 0, 0}
718		};
719
720		/* getopt_long stores the option index here. */
721		int option_index = 0;
722
723		c = getopt_long(argc, argv, "hr:stv", long_options, &option_index);
724
725		/* detect the end of the options. */
726		if (c == -1)
727			break;
728
729		switch (c) {
730		case 'h':
731			ikm_usage(0, monitor_name, "help:");
732			break;
733		case 'r':
734			config_reactor = optarg;
735			break;
736		case 's':
737			config_my_pid = -1;
738			break;
739		case 't':
740			config_trace = 1;
741			break;
742		case 'v':
743			config_debug = 1;
744			break;
745		}
746	}
747
748	if (config_reactor) {
749		config_initial_reactor = ikm_get_current_reactor(monitor_name);
750		if (!config_initial_reactor)
751			ikm_usage(1, monitor_name,
752				  "ikm: failed to read current reactor, are reactors enabled?");
753
754		retval = ikm_write_reactor(monitor_name, config_reactor);
755		if (retval <= 0)
756			ikm_usage(1, monitor_name,
757				  "ikm: failed to set %s reactor, is it available?",
758				  config_reactor);
759	}
760
761	debug_msg("ikm: my pid is %d\n", config_my_pid);
762
763	return 0;
764}
765
766/**
767 * ikm_run_monitor - apply configs and run the monitor
768 *
769 * Returns 1 if a monitor was found an executed, 0 if no
770 * monitors were found, or -1 on error.
771 */
772int ikm_run_monitor(char *monitor_name, int argc, char **argv)
773{
774	struct trace_instance *inst = NULL;
775	char *nested_name, full_name[2*MAX_DA_NAME_LEN];
776	int retval;
777
778	nested_name = strstr(monitor_name, ":");
779	if (nested_name)
780		++nested_name;
781	else
782		nested_name = monitor_name;
783
784	retval = __ikm_find_monitor_name(monitor_name, full_name);
785	if (!retval)
786		return 0;
787	if (retval < 0) {
788		err_msg("ikm: error finding monitor %s\n", nested_name);
789		return -1;
790	}
791
792	retval = __ikm_read_enable(full_name);
793	if (retval) {
794		err_msg("ikm: monitor %s (in-kernel) is already enabled\n", nested_name);
795		return -1;
796	}
797
798	/* we should be good to go */
799	retval = parse_arguments(full_name, argc, argv);
800	if (retval)
801		ikm_usage(1, nested_name, "ikm: failed parsing arguments");
802
803	if (config_trace) {
804		inst = ikm_setup_trace_instance(nested_name);
805		if (!inst)
806			return -1;
807	}
808
809	retval = ikm_enable(full_name);
810	if (retval < 0)
811		goto out_free_instance;
812
813	if (config_trace)
814		ikm_print_header(inst->seq);
815
816	while (!should_stop()) {
817		if (config_trace) {
818			retval = tracefs_iterate_raw_events(inst->tep,
819							    inst->inst,
820							    NULL,
821							    0,
822							    collect_registered_events,
823							    inst);
824			if (retval) {
825				err_msg("ikm: error reading trace buffer\n");
826				break;
827			}
828		}
829
830		sleep(1);
831	}
832
833	ikm_disable(full_name);
834	ikm_destroy_trace_instance(inst);
835
836	if (config_reactor && config_initial_reactor)
837		ikm_write_reactor(full_name, config_initial_reactor);
838
839	return 1;
840
841out_free_instance:
842	ikm_destroy_trace_instance(inst);
843	if (config_reactor && config_initial_reactor)
844		ikm_write_reactor(full_name, config_initial_reactor);
845	return -1;
846}
~/ Read-only file preview.