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 19 static int config_has_id; 20 static int config_is_container; 21 static int config_my_pid; 22 static int config_trace; 23 24 static char *config_initial_reactor; 25 static 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 */ 35 static 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 */ 59 static 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 90 out_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 */ 100 static 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 */ 120 static 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 */ 142 static 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 */ 152 static 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 */ 163 static 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 */ 186 static 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 */ 232 static 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 */ 250 static 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 */ 274 static 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 303 out_free: 304 free(reactors); 305 306 return curr_reactor; 307 } 308 309 static 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 */ 337 int 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 384 static 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 */ 415 static int 416 ikm_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 && (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 */ 476 static int 477 ikm_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 522 static 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 552 static 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); 575 out_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 */ 587 static 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 618 out_inst: 619 trace_instance_destroy(inst); 620 out_free: 621 free(inst); 622 out_err: 623 return NULL; 624 } 625 626 /** 627 * ikm_destroy_trace_instance - destroy a previously created instance 628 */ 629 static 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 */ 641 static 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 */ 668 static 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 */ 704 static 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 = 0; 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 */ 772 int 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 841 out_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 } 847