1 /******************************************************************************
2 * Copyright (c) 2000-2016 Ericsson Telecom AB
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
20 * Szabo, Janos Zoltan – initial implementation
22 * Zalanyi, Balazs Andor
23 * Roland Gecse - author
25 ******************************************************************************/
27 // Description: Implementation file for Cli
29 //----------------------------------------------------------------------------
31 #include "../mctr/MainController.h"
34 #include "../editline/libedit/src/editline/readline.h"
40 #include <arpa/inet.h>
41 #include "../../common/version_internal.h"
42 #include "../../common/memory.h"
43 #include "../../common/config_preproc.h"
44 #include "../../core/DebugCommands.hh"
46 #define PROMPT "MC2> "
47 #define CMTC_TEXT "cmtc"
48 #define SMTC_TEXT "smtc"
49 #define EMTC_TEXT "emtc"
50 #define STOP_TEXT "stop"
51 #define PAUSE_TEXT "pause"
52 #define CONTINUE_TEXT "continue"
53 #define INFO_TEXT "info"
54 #define HELP_TEXT "help"
55 #define RECONF_TEXT "reconf"
56 #define LOG_TEXT "log"
57 #define SHELL_TEXT "!"
58 #define EXIT_TEXT "quit"
59 #define EXIT_TEXT2 "exit"
60 #define BATCH_TEXT "batch"
61 #define SHELL_ESCAPE '!'
62 #define TTCN3_HISTORY_FILENAME ".ttcn3_history"
64 using mctr::MainController
;
68 * shell_mode == TRUE while editing a command line that is passed to the shell
70 static boolean shell_mode
;
74 callback_t callback_function
;
76 const char *description
;
79 static const Command command_list
[] = {
80 { CMTC_TEXT
, &Cli::cmtcCallback
, CMTC_TEXT
" [hostname]",
82 { SMTC_TEXT
, &Cli::smtcCallback
,
83 SMTC_TEXT
" [module_name[[.control]|.testcase_name|.*]",
84 "Start MTC with control part, test case or all test cases." },
85 { STOP_TEXT
, &Cli::stopCallback
, STOP_TEXT
,
86 "Stop test execution." },
87 { PAUSE_TEXT
, &Cli::pauseCallback
, PAUSE_TEXT
" [on|off]",
88 "Set whether to interrupt test execution after each "
90 { CONTINUE_TEXT
, &Cli::continueCallback
, CONTINUE_TEXT
,
91 "Resumes interrupted test execution." },
92 { EMTC_TEXT
, &Cli::emtcCallback
, EMTC_TEXT
, "Terminate MTC." },
93 { LOG_TEXT
, &Cli::logCallback
, LOG_TEXT
" [on|off]",
94 "Enable/disable console logging." },
95 { INFO_TEXT
, &Cli::infoCallback
, INFO_TEXT
,
96 "Display test configuration information." },
97 { RECONF_TEXT
, &Cli::reconfCallback
, RECONF_TEXT
" [config_file]",
98 "Reload configuration file." },
99 { HELP_TEXT
, &Cli::helpCallback
, HELP_TEXT
" <command>",
100 "Display help on command." },
101 { SHELL_TEXT
, &Cli::shellCallback
, SHELL_TEXT
"[shell cmds]",
102 "Execute commands in subshell." },
103 { EXIT_TEXT
, &Cli::exitCallback
, EXIT_TEXT
, "Exit Main Controller." },
104 { EXIT_TEXT2
, &Cli::exitCallback
, EXIT_TEXT2
, "Exit Main Controller." },
105 { BATCH_TEXT
, &Cli::executeBatchFile
, BATCH_TEXT
" <batch_file>",
106 "Run commands from batch file." },
107 { NULL
, NULL
, NULL
, NULL
}
110 struct DebugCommand
{
113 const char *synopsis
;
114 const char *description
;
117 static const DebugCommand debug_command_list
[] = {
118 { D_SWITCH_TEXT
, D_SWITCH
, D_SWITCH_TEXT
" on|off",
119 "Switch the debugger on or off." },
120 { D_SET_BREAKPOINT_TEXT
, D_SET_BREAKPOINT
,
121 D_SET_BREAKPOINT_TEXT
" <module> <line>|<function> [<batch_file>]",
122 "Add a breakpoint at the specified location, or change the batch file of "
123 "an existing breakpoint." },
124 { D_REMOVE_BREAKPOINT_TEXT
, D_REMOVE_BREAKPOINT
,
125 D_REMOVE_BREAKPOINT_TEXT
" all|<module> [all|<line>|<function>]",
126 "Remove a breakpoint, or all breakpoints from a module, or all breakpoints "
127 "from all modules." },
128 { D_SET_AUTOMATIC_BREAKPOINT_TEXT
, D_SET_AUTOMATIC_BREAKPOINT
,
129 D_SET_AUTOMATIC_BREAKPOINT_TEXT
" error|fail on|off [<batch_file>]",
130 "Switch an automatic breakpoint (truggered by an event) on or off, and/or "
131 "change its batch file." },
132 { D_SET_OUTPUT_TEXT
, D_SET_OUTPUT
,
133 D_SET_OUTPUT_TEXT
" console|file|both [file_name]",
134 "Set the output of the debugger." },
135 { D_SET_GLOBAL_BATCH_FILE_TEXT
, D_SET_GLOBAL_BATCH_FILE
,
136 D_SET_GLOBAL_BATCH_FILE_TEXT
" on|off [batch_file_name]",
137 "Set whether a batch file should be executed automatically when test execution "
138 "is halted (breakpoint-specific batch files override this setting)." },
139 { D_FUNCTION_CALL_CONFIG_TEXT
, D_FUNCTION_CALL_CONFIG
,
140 D_FUNCTION_CALL_CONFIG_TEXT
" file|<limit>|all [<file_name>]",
141 "Configure the storing of function call data." },
142 { D_PRINT_SETTINGS_TEXT
, D_PRINT_SETTINGS
, D_PRINT_SETTINGS_TEXT
,
143 "Prints the debugger's settings." },
144 { D_LIST_COMPONENTS_TEXT
, D_LIST_COMPONENTS
, D_LIST_COMPONENTS_TEXT
,
145 "List the test components currently running debuggable code." },
146 { D_SET_COMPONENT_TEXT
, D_SET_COMPONENT
,
147 D_SET_COMPONENT_TEXT
" <component name>|<component_reference>",
148 "Set the test component to print debug information from." },
149 { D_PRINT_CALL_STACK_TEXT
, D_PRINT_CALL_STACK
, D_PRINT_CALL_STACK_TEXT
,
150 "Print call stack." },
151 { D_SET_STACK_LEVEL_TEXT
, D_SET_STACK_LEVEL
, D_SET_STACK_LEVEL_TEXT
" <level>",
152 "Set the stack level to print debug information from." },
153 { D_LIST_VARIABLES_TEXT
, D_LIST_VARIABLES
,
154 D_LIST_VARIABLES_TEXT
" [local|global|comp|all] [pattern]",
155 "List variable names." },
156 { D_PRINT_VARIABLE_TEXT
, D_PRINT_VARIABLE
,
157 D_PRINT_VARIABLE_TEXT
" <variable_name>|$ [{ <variable_name>|$}]",
158 "Print current value of one or more variables ('$' is substituted with the "
159 "result of the last " D_LIST_VARIABLES_TEXT
" command)." },
160 { D_OVERWRITE_VARIABLE_TEXT
, D_OVERWRITE_VARIABLE
,
161 D_OVERWRITE_VARIABLE_TEXT
" <variable_name> <value>",
162 "Overwrite the current value of a variable." },
163 { D_PRINT_FUNCTION_CALLS_TEXT
, D_PRINT_FUNCTION_CALLS
,
164 D_PRINT_FUNCTION_CALLS_TEXT
" [all|<amount>]",
165 "Print function call data." },
166 { D_STEP_OVER_TEXT
, D_STEP_OVER
, D_STEP_OVER_TEXT
,
167 "Resume test execution until the next line of code (in this function or the "
168 "caller function)." },
169 { D_STEP_INTO_TEXT
, D_STEP_INTO
, D_STEP_INTO_TEXT
,
170 "Resume test execution until the next line of code (on any stack level)." },
171 { D_STEP_OUT_TEXT
, D_STEP_OUT
, D_STEP_OUT_TEXT
,
172 "Resume test execution until the next line of code in the caller function." },
173 { D_RUN_TO_CURSOR_TEXT
, D_RUN_TO_CURSOR
,
174 D_RUN_TO_CURSOR_TEXT
" <module> <line>|<function>",
175 "Resume test execution until the specified location." },
176 { D_HALT_TEXT
, D_HALT
, D_HALT_TEXT
, "Halt test execution." },
177 { D_CONTINUE_TEXT
, D_CONTINUE
, D_CONTINUE_TEXT
, "Resume halted test execution." },
178 { D_EXIT_TEXT
, D_EXIT
, D_EXIT_TEXT
" test|all",
179 "Exit the current test or the execution of all tests." },
180 { NULL
, D_ERROR
, NULL
, NULL
}
185 loggingEnabled
= TRUE
;
187 waitState
= WAIT_NOTHING
;
188 executeListIndex
= 0;
190 if (pthread_mutex_init(&mutex
, NULL
)) {
191 perror("Cli::Cli: pthread_mutex_init failed.");
194 if (pthread_cond_init(&cond
, NULL
)) {
195 perror("Cli::Cli: pthread_cond_init failed.");
198 cfg_file_name
= NULL
;
203 pthread_mutex_destroy(&mutex
);
204 pthread_cond_destroy(&cond
);
208 //----------------------------------------------------------------------------
211 int Cli::enterLoop(int argc
, char *argv
[])
213 // Parameter check: mctr [config file name]
222 cfg_file_name
= mcopystr(argv
[1]);
223 printf("Using configuration file: %s\n", cfg_file_name
);
224 if (process_config_read_file(cfg_file_name
, &mycfg
)) {
225 puts("Error was found in the configuration file. Exiting.");
230 MainController::set_kill_timer(mycfg
.kill_timer
);
232 for (int i
= 0; i
< mycfg
.group_list_len
; ++i
) {
233 MainController::add_host(mycfg
.group_list
[i
].group_name
,
234 mycfg
.group_list
[i
].host_name
);
237 for (int i
= 0; i
< mycfg
.component_list_len
; ++i
) {
238 MainController::assign_component(mycfg
.component_list
[i
].host_or_group
,
239 mycfg
.component_list
[i
].component
);
245 if (mycfg
.num_hcs
<= 0) ret_val
= interactiveMode();
246 else ret_val
= batchMode();
252 //----------------------------------------------------------------------------
255 void Cli::status_change()
258 if (waitState
!= WAIT_NOTHING
&& conditionHolds(waitState
)) {
259 waitState
= WAIT_NOTHING
;
265 //----------------------------------------------------------------------------
268 void Cli::error(int /*severity*/, const char *message
)
270 printf("Error: %s\n", message
);
272 // TODO: Error handling based on the MC state where the error happened
275 //----------------------------------------------------------------------------
278 void Cli::notify(const struct timeval
*timestamp
, const char *source
,
279 int /*severity*/, const char *message
)
283 switch(mycfg
.tsformat
){
284 case TSF_TIME
: // handled together
287 time_t tv_sec
= timestamp
->tv_sec
;
288 struct tm
*lt
= localtime(&tv_sec
);
290 printf("localtime() call failed.");
291 printf("%s: %s\n", source
, message
);
295 if (mycfg
.tsformat
== TSF_TIME
) {
296 printf("%02d:%02d:%02d.%06ld %s: %s\n",
297 lt
->tm_hour
, lt
->tm_min
, lt
->tm_sec
, timestamp
->tv_usec
,source
, message
);
299 static const char * const month_names
[] = { "Jan", "Feb", "Mar",
300 "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
301 printf("%4d/%s/%02d %02d:%02d:%02d.%06ld %s: %s\n",
302 lt
->tm_year
+ 1900, month_names
[lt
->tm_mon
],
303 lt
->tm_mday
, lt
->tm_hour
, lt
->tm_min
, lt
->tm_sec
,
311 printf("%ld.%06ld %s: %s\n", (long)timestamp
->tv_sec
, timestamp
->tv_usec
, source
, message
);
316 printf("%s: %s\n", source
, message
);
323 //----------------------------------------------------------------------------
330 //----------------------------------------------------------------------------
333 void Cli::printWelcome()
336 "*************************************************************************\n"
337 "* TTCN-3 Test Executor - Main Controller 2 *\n"
338 "* Version: %-40s *\n"
339 "* Copyright (c) 2000-2016 Ericsson Telecom AB *\n"
340 "* All rights reserved. This program and the accompanying materials *\n"
341 "* are made available under the terms of the Eclipse Public License v1.0 *\n"
342 "* which accompanies this distribution, and is available at *\n"
343 "* http://www.eclipse.org/legal/epl-v10.html *\n"
344 "*************************************************************************\n"
345 "\n", PRODUCT_NUMBER
);
348 void Cli::printUsage(const char *prg_name
)
351 "TTCN-3 Test Executor - Main Controller 2\n"
352 "Version: " PRODUCT_NUMBER
"\n\n"
353 "usage: %s [configuration_file]\n"
354 "where: the optional 'configuration_file' parameter specifies the name "
355 "and\nlocation of the main controller configuration file"
359 //----------------------------------------------------------------------------
362 int Cli::interactiveMode()
364 // Initialize history library
367 const char *home_directory
= getenv("HOME");
368 if(home_directory
== NULL
)
369 home_directory
= ".";
370 char *ttcn3_history_filename
= mprintf("%s/%s", home_directory
,
371 TTCN3_HISTORY_FILENAME
);
372 // Read history from file, don't bother if it does not exist!
373 read_history(ttcn3_history_filename
);
374 // Set our own command completion function
375 rl_completion_entry_function
= (Function
*)completeCommand
;
376 // Override rl_getc() in order to detect shell mode
377 rl_getc_function
= getcWithShellDetection
;
379 // TCP listen port parameter, returns TCP port on which it listens!
380 // Returns 0 on error.
381 if (MainController::start_session(mycfg
.local_addr
, mycfg
.tcp_listen_port
,
382 mycfg
.unix_sockets_enabled
) == 0) {
383 puts("Initialization of TCP server failed. Exiting.");
384 Free(ttcn3_history_filename
);
389 char *line_read
= readline(PROMPT
);
390 if (line_read
!= NULL
) {
394 add_history(line_read
); // history maintains its own copy
395 // process and free line
396 processCommand(line_read
);
407 if (write_history(ttcn3_history_filename
))
408 perror("Could not save history.");
409 Free(ttcn3_history_filename
);
416 printf("Entering batch mode. Waiting for %d HC%s to connect...\n",
417 mycfg
.num_hcs
, mycfg
.num_hcs
> 1 ? "s" : "");
418 if (mycfg
.execute_list_len
<= 0) {
419 puts("No [EXECUTE] section was given in the configuration file. "
423 boolean error_flag
= FALSE
;
424 // start to listen on TCP port
425 if (MainController::start_session(mycfg
.local_addr
, mycfg
.tcp_listen_port
,
426 mycfg
.unix_sockets_enabled
) == 0) {
427 puts("Initialization of TCP server failed. Exiting.");
430 waitMCState(WAIT_HC_CONNECTED
);
431 // download config file
432 MainController::configure(mycfg
.config_read_buffer
);
433 waitMCState(WAIT_ACTIVE
);
434 if (MainController::get_state() != mctr::MC_ACTIVE
) {
435 puts("Error during initialization. Cannot continue in batch mode.");
439 // create MTC on firstly connected HC
440 MainController::create_mtc(0);
441 waitMCState(WAIT_MTC_CREATED
);
442 if (MainController::get_state() != mctr::MC_READY
) {
443 puts("Creation of MTC failed. Cannot continue in batch mode.");
448 // execute each item of the list
449 for (int i
= 0; i
< mycfg
.execute_list_len
; i
++) {
451 waitMCState(WAIT_MTC_READY
);
452 if (MainController::get_state() != mctr::MC_READY
) {
453 puts("MTC terminated unexpectedly. Cannot continue in batch "
462 MainController::exit_mtc();
463 waitMCState(WAIT_MTC_TERMINATED
);
465 // now MC must be in state MC_ACTIVE anyway
467 MainController::shutdown_session();
468 waitMCState(WAIT_SHUTDOWN_COMPLETE
);
469 if (error_flag
) return EXIT_FAILURE
;
470 else return EXIT_SUCCESS
;
473 //----------------------------------------------------------------------------
476 void Cli::processCommand(char *line_read
)
478 for (const Command
*command
= command_list
; command
->name
!= NULL
;
480 size_t command_name_len
= strlen(command
->name
);
481 if (!strncmp(line_read
, command
->name
, command_name_len
)) {
482 memset(line_read
, ' ', command_name_len
);
484 (this->*command
->callback_function
)(line_read
);
488 for (const DebugCommand
* command
= debug_command_list
; command
->name
!= NULL
;
490 size_t command_name_len
= strlen(command
->name
);
491 if (!strncmp(line_read
, command
->name
, command_name_len
)) {
492 memset(line_read
, ' ', command_name_len
);
494 MainController::debug_command(command
->commandID
, line_read
);
495 if (waitState
== WAIT_EXECUTE_LIST
&& command
->commandID
== D_EXIT
&&
496 !strcmp(line_read
, "all")) {
497 // stop executing the list from the config file
498 waitState
= WAIT_NOTHING
;
503 puts("Unknown command, try again...");
506 //----------------------------------------------------------------------------
508 // Create Main Test Component
510 void Cli::cmtcCallback(const char *arguments
)
513 if(*arguments
== 0) hostIndex
= 0;
515 hostIndex
= getHostIndex(arguments
);
516 if (hostIndex
< 0) return;
518 switch (MainController::get_state()) {
519 case mctr::MC_LISTENING
:
520 case mctr::MC_LISTENING_CONFIGURED
:
521 puts("Waiting for HC to connect...");
522 waitMCState(WAIT_HC_CONNECTED
);
523 case mctr::MC_HC_CONNECTED
:
524 MainController::configure(mycfg
.config_read_buffer
);
525 waitMCState(WAIT_ACTIVE
);
526 if (MainController::get_state() != mctr::MC_ACTIVE
) {
527 puts("Error during initialization. Cannot create MTC.");
530 case mctr::MC_ACTIVE
:
531 MainController::create_mtc(hostIndex
);
532 waitMCState(WAIT_MTC_CREATED
);
535 puts("MTC already exists.");
540 //----------------------------------------------------------------------------
542 // Start Main Test Component and execute the
543 // a) control part or testcase (or *) given in arguments
544 // b) EXECUTE part of supplied configuration file -- if arguments==NULL
546 void Cli::smtcCallback(const char *arguments
)
548 switch (MainController::get_state()) {
549 case mctr::MC_LISTENING
:
550 case mctr::MC_LISTENING_CONFIGURED
:
551 case mctr::MC_HC_CONNECTED
:
552 case mctr::MC_ACTIVE
:
553 puts("MTC does not exist.");
556 if (*arguments
== 0) {
557 // Execute configuration file's execute section
558 if (mycfg
.execute_list_len
> 0) {
559 puts("Executing all items of [EXECUTE] section.");
560 waitState
= WAIT_EXECUTE_LIST
;
561 executeListIndex
= 0;
564 puts("No [EXECUTE] section was given in the configuration "
567 } else { // Check the arguments
568 size_t doti
= 0, alen
= strlen(arguments
), state
= 0;
569 for (size_t r
= 0; r
< alen
; r
++) {
570 switch (arguments
[r
]) {
581 if(state
> 1) { // incorrect argument
582 puts("Incorrect argument format.");
583 helpCallback(SMTC_TEXT
);
585 if(state
== 0) { // only modulename is given in arguments
586 MainController::execute_control(arguments
);
587 } else { // modulename.something in arguments
588 expstring_t arg_copy
= mcopystr(arguments
);
589 arg_copy
[doti
++] = '\0';
590 if (!strcmp(arg_copy
+ doti
, "*"))
591 MainController::execute_testcase(arg_copy
, NULL
);
592 else if (!strcmp(arg_copy
+ doti
, "control"))
593 MainController::execute_control(arg_copy
);
594 else MainController::execute_testcase(arg_copy
,
602 puts("MTC is busy.");
607 //----------------------------------------------------------------------------
609 // Stops test execution
611 void Cli::stopCallback(const char *arguments
)
613 if (*arguments
== 0) {
614 switch (MainController::get_state()) {
615 case mctr::MC_TERMINATING_TESTCASE
:
616 case mctr::MC_EXECUTING_CONTROL
:
617 case mctr::MC_EXECUTING_TESTCASE
:
618 case mctr::MC_PAUSED
:
619 MainController::stop_execution();
620 if (waitState
== WAIT_EXECUTE_LIST
) waitState
= WAIT_NOTHING
;
623 puts("Tests are not running.");
625 } else helpCallback(STOP_TEXT
);
628 //----------------------------------------------------------------------------
630 // Sets whether to interrupt test execution after testcase
632 void Cli::pauseCallback(const char *arguments
)
634 if (arguments
[0] != '\0') {
635 if (!strcmp(arguments
, "on")) {
636 if (!MainController::get_stop_after_testcase()) {
637 MainController::stop_after_testcase(TRUE
);
638 puts("Pause function is enabled. "
639 "Execution will stop at the end of each testcase.");
640 } else puts("Pause function is already enabled.");
641 } else if (!strcmp(arguments
, "off")) {
642 if (MainController::get_stop_after_testcase()) {
643 MainController::stop_after_testcase(FALSE
);
644 puts("Pause function is disabled. "
645 "Execution will continue at the end of each testcase.");
646 } else puts("Pause function is already disabled.");
647 } else helpCallback(PAUSE_TEXT
);
648 } else printf("Pause function is %s.\n",
649 MainController::get_stop_after_testcase() ? "enabled" : "disabled");
652 //----------------------------------------------------------------------------
654 // Resumes interrupted test execution
656 void Cli::continueCallback(const char *arguments
)
658 if (*arguments
== 0) {
659 switch (MainController::get_state()) {
660 case mctr::MC_TERMINATING_TESTCASE
:
661 case mctr::MC_EXECUTING_CONTROL
:
662 case mctr::MC_EXECUTING_TESTCASE
:
663 puts("Execution is not paused.");
665 case mctr::MC_PAUSED
:
666 MainController::continue_testcase();
669 puts("Tests are not running.");
671 } else helpCallback(CONTINUE_TEXT
);
674 //----------------------------------------------------------------------------
676 // Exit Main Test Component
678 void Cli::emtcCallback(const char *arguments
)
680 if(*arguments
== 0) {
681 switch (MainController::get_state()) {
682 case mctr::MC_LISTENING
:
683 case mctr::MC_LISTENING_CONFIGURED
:
684 case mctr::MC_HC_CONNECTED
:
685 case mctr::MC_ACTIVE
:
686 puts("MTC does not exist.");
689 MainController::exit_mtc();
690 waitMCState(WAIT_MTC_TERMINATED
);
693 puts("MTC cannot be terminated.");
696 helpCallback(EMTC_TEXT
);
700 //----------------------------------------------------------------------------
702 // Controls console logging
704 void Cli::logCallback(const char *arguments
)
706 if (arguments
[0] != '\0') {
707 if (!strcmp(arguments
, "on")) {
708 loggingEnabled
= TRUE
;
709 puts("Console logging is enabled.");
710 } else if (!strcmp(arguments
, "off")) {
711 loggingEnabled
= FALSE
;
712 puts("Console logging is disabled.");
713 } else helpCallback(LOG_TEXT
);
714 } else printf("Console logging is %s.\n",
715 loggingEnabled
? "enabled" : "disabled");
718 //----------------------------------------------------------------------------
720 // Print connection information
722 void Cli::infoCallback(const char *arguments
)
724 if (*arguments
== 0) printInfo();
725 else helpCallback(INFO_TEXT
);
728 //----------------------------------------------------------------------------
730 // Reconfigure MC and HCs
732 void Cli::reconfCallback(const char *arguments
)
734 if (!MainController::start_reconfiguring()) {
737 if (*arguments
!= 0) {
739 cfg_file_name
= mcopystr(arguments
);
742 printf("Using configuration file: %s\n", cfg_file_name
);
743 if (process_config_read_file(cfg_file_name
, &mycfg
)) {
744 puts("Error was found in the configuration file. Exiting.");
750 MainController::set_kill_timer(mycfg
.kill_timer
);
752 for (int i
= 0; i
< mycfg
.group_list_len
; ++i
) {
753 MainController::add_host(mycfg
.group_list
[i
].group_name
,
754 mycfg
.group_list
[i
].host_name
);
757 for (int i
= 0; i
< mycfg
.component_list_len
; ++i
) {
758 MainController::assign_component(mycfg
.component_list
[i
].host_or_group
,
759 mycfg
.component_list
[i
].component
);
762 if (MainController::get_state() == mctr::MC_RECONFIGURING
) {
763 MainController::configure(mycfg
.config_read_buffer
);
768 //----------------------------------------------------------------------------
770 // Print general help or help on command usage to console
772 void Cli::helpCallback(const char *arguments
)
774 if (*arguments
== 0) {
775 puts("Help is available for the following commands:");
776 for (const Command
*command
= command_list
;
777 command
->name
!= NULL
; command
++) {
778 printf("%s ", command
->name
);
780 for (const DebugCommand
*command
= debug_command_list
;
781 command
->name
!= NULL
; command
++) {
782 printf("%s ", command
->name
);
786 for (const Command
*command
= command_list
;
787 command
->name
!= NULL
; command
++) {
788 if (!strncmp(arguments
, command
->name
,
789 strlen(command
->name
))) {
790 printf("%s usage: %s\n%s\n", command
->name
,
792 command
->description
);
796 for (const DebugCommand
*command
= debug_command_list
;
797 command
->name
!= NULL
; command
++) {
798 if (!strncmp(arguments
, command
->name
,
799 strlen(command
->name
))) {
800 printf("%s usage: %s\n%s\n", command
->name
,
802 command
->description
);
806 printf("No help for %s.\n", arguments
);
810 //----------------------------------------------------------------------------
812 // Pass command to shell
814 void Cli::shellCallback(const char *arguments
)
816 if(system(arguments
) == -1) {
817 perror("Error executing command in subshell.");
821 //----------------------------------------------------------------------------
823 // User initiated MC termination, exits the program.
824 // Save history into file and terminate CLI session.
826 void Cli::exitCallback(const char *arguments
)
828 if (*arguments
== 0) {
829 switch (MainController::get_state()) {
831 case mctr::MC_RECONFIGURING
:
832 MainController::exit_mtc();
833 waitMCState(WAIT_MTC_TERMINATED
);
834 case mctr::MC_LISTENING
:
835 case mctr::MC_LISTENING_CONFIGURED
:
836 case mctr::MC_HC_CONNECTED
:
837 case mctr::MC_ACTIVE
:
838 MainController::shutdown_session();
839 waitMCState(WAIT_SHUTDOWN_COMPLETE
);
843 puts("Cannot exit until execution is finished.");
846 helpCallback(EXIT_TEXT
);
850 void Cli::executeBatchFile(const char* filename
)
852 FILE* fp
= fopen(filename
, "r");
854 printf("Failed to open file '%s' for reading.\n", filename
);
858 printf("Executing batch file '%s'.\n", filename
);
861 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
862 size_t len
= strlen(line
);
863 if (line
[len
- 1] == '\n') {
864 line
[len
- 1] = '\0';
868 printf("%s\n", line
);
869 processCommand(line
);
873 printf("Error occurred while reading batch file '%s' (error code: %d).\n",
874 filename
, ferror(fp
));
879 //----------------------------------------------------------------------------
881 // Command completion function implementation for readline() library.
882 // Heavily uses the ``static Command command_list[]'' array!
884 char *Cli::completeCommand(const char *prefix
, int state
)
886 static int command_index
;
887 static int debug_command_index
;
888 static size_t prefix_len
;
889 const char *command_name
;
892 return rl_filename_completion_function(prefix
, state
);
896 debug_command_index
= 0;
897 prefix_len
= strlen(prefix
);
900 while((command_name
= command_list
[command_index
].name
)) {
902 if(strncmp(prefix
, command_name
, prefix_len
) == 0) {
903 // Must allocate buffer for returned string (readline frees it)
904 return strdup(command_name
);
908 while ((command_name
= debug_command_list
[debug_command_index
].name
)) {
909 ++debug_command_index
;
910 if (strncmp(prefix
, command_name
, prefix_len
) == 0) {
911 // Must allocate buffer for returned string (readline frees it)
912 return strdup(command_name
);
919 //----------------------------------------------------------------------------
921 // Character input function implementation for readline() library.
923 int Cli::getcWithShellDetection(FILE *fp
)
925 int input_char
= getc(fp
);
926 if(input_char
== SHELL_ESCAPE
)
932 //----------------------------------------------------------------------------
935 void Cli::stripLWS(char *input_text
)
937 if(input_text
== NULL
) {
938 puts("stripLWS() called with NULL.");
939 exit(EXIT_FAILURE
); // This shall never happen
941 size_t head_index
, tail_index
, input_len
= strlen(input_text
);
942 if(input_len
< 1) return;
943 for(head_index
= 0; isspace(input_text
[head_index
]); head_index
++) ;
944 for(tail_index
= input_len
- 1; tail_index
>= head_index
&&
945 isspace(input_text
[tail_index
]); tail_index
--) ;
946 size_t output_len
= tail_index
- head_index
+ 1;
947 memmove(input_text
, input_text
+ head_index
, output_len
);
948 memset(input_text
+ output_len
, 0, input_len
- output_len
);
951 const char *Cli::verdict2str(verdicttype verdict
)
969 //----------------------------------------------------------------------------
972 void Cli::printInfo()
974 puts("MC information:");
975 printf(" MC state: %s\n",
976 MainController::get_mc_state_name(MainController::get_state()));
977 puts(" host information:");
979 for ( ; ; host_index
++) {
980 mctr::host_struct
*host
= MainController::get_host_data(host_index
);
982 printf(" - %s", host
->hostname
);
983 const char *ip_addr
= host
->ip_addr
->get_addr_str();
984 // if the hostname differs from the IP address
985 // (i.e. the host has a DNS entry)
986 if (strcmp(ip_addr
, host
->hostname
)) printf(" [%s]", ip_addr
);
987 // if the local hostname differs from the prefix of the DNS name
988 if (strncmp(host
->hostname
, host
->hostname_local
,
989 strlen(host
->hostname_local
)))
990 printf(" (%s)", host
->hostname_local
);
992 printf(" operating system: %s %s on %s\n", host
->system_name
,
993 host
->system_release
, host
->machine_type
);
994 printf(" HC state: %s\n",
995 MainController::get_hc_state_name(host
->hc_state
));
997 puts(" test component information:");
998 // make a copy of the array containing component references
999 int n_components
= host
->n_components
;
1000 component
*components
= (component
*)Malloc(n_components
*
1002 memcpy(components
, host
->components
, n_components
*
1004 // the host structure has to be released in order to get access
1005 // to the component structures
1006 MainController::release_data();
1007 for (int component_index
= 0; component_index
< n_components
;
1008 component_index
++) {
1009 mctr::component_struct
*comp
= MainController::
1010 get_component_data(components
[component_index
]);
1011 // if the component has a name
1012 if (comp
->comp_name
!= NULL
)
1013 printf(" - name: %s, component reference: %d\n",
1014 comp
->comp_name
, comp
->comp_ref
);
1015 else printf(" - component reference: %d\n",
1017 if (comp
->comp_type
.definition_name
!= NULL
) {
1018 printf(" component type: ");
1019 if (comp
->comp_type
.module_name
!= NULL
)
1020 printf("%s.", comp
->comp_type
.module_name
);
1021 printf("%s\n", comp
->comp_type
.definition_name
);
1023 printf(" state: %s\n",
1024 MainController::get_tc_state_name(comp
->tc_state
));
1025 if (comp
->tc_fn_name
.definition_name
!= NULL
) {
1026 printf(" executed %s: ",
1027 comp
->comp_ref
== MTC_COMPREF
?
1028 "test case" : "function");
1029 if (comp
->tc_fn_name
.module_name
!= NULL
)
1030 printf("%s.", comp
->tc_fn_name
.module_name
);
1031 printf("%s\n", comp
->tc_fn_name
.definition_name
);
1033 if (comp
->tc_state
== mctr::TC_EXITING
||
1034 comp
->tc_state
== mctr::TC_EXITED
)
1035 printf(" local verdict: %s\n",
1036 verdict2str(comp
->local_verdict
));
1037 MainController::release_data();
1039 if (n_components
== 0) puts(" no components on this host");
1042 MainController::release_data();
1046 if (host_index
== 0) puts(" no HCs are connected");
1047 printf(" pause function: %s\n", MainController::get_stop_after_testcase() ?
1048 "enabled" : "disabled");
1049 printf(" console logging: %s\n", loggingEnabled
?
1050 "enabled" : "disabled");
1054 //----------------------------------------------------------------------------
1059 if (pthread_mutex_lock(&mutex
)) {
1060 perror("Cli::lock: pthread_mutex_lock failed.");
1067 if (pthread_mutex_unlock(&mutex
)) {
1068 perror("Cli::unlock: pthread_mutex_unlock failed.");
1075 if (pthread_cond_wait(&cond
, &mutex
)) {
1076 perror("Cli::wait: pthread_cond_wait failed.");
1083 if (pthread_cond_signal(&cond
)) {
1084 perror("Cli::signal: pthread_cond_signal failed.");
1089 void Cli::waitMCState(waitStateEnum newWaitState
)
1092 if (newWaitState
!= WAIT_NOTHING
) {
1093 if (conditionHolds(newWaitState
)) {
1094 waitState
= WAIT_NOTHING
;
1096 waitState
= newWaitState
;
1100 fputs("Cli::waitMCState: invalid argument", stderr
);
1106 boolean
Cli::conditionHolds(waitStateEnum askedState
)
1108 switch (askedState
) {
1109 case WAIT_HC_CONNECTED
:
1110 if (MainController::get_state() == mctr::MC_HC_CONNECTED
) {
1111 if (mycfg
.num_hcs
> 0) {
1112 return MainController::get_nof_hosts() >= mycfg
.num_hcs
;
1115 } else return FALSE
;
1117 switch (MainController::get_state()) {
1118 case mctr::MC_ACTIVE
: // normal case
1119 case mctr::MC_HC_CONNECTED
: // error happened with config file
1120 case mctr::MC_LISTENING
: // even more strange situations
1125 case WAIT_MTC_CREATED
:
1126 case WAIT_MTC_READY
:
1127 switch (MainController::get_state()) {
1128 case mctr::MC_READY
: // normal case
1129 case mctr::MC_ACTIVE
: // MTC crashed unexpectedly
1130 case mctr::MC_LISTENING_CONFIGURED
: // MTC and all HCs are crashed
1132 case mctr::MC_HC_CONNECTED
: // even more strange situations
1137 case WAIT_MTC_TERMINATED
:
1138 return MainController::get_state() == mctr::MC_ACTIVE
;
1139 case WAIT_SHUTDOWN_COMPLETE
:
1140 return MainController::get_state() == mctr::MC_INACTIVE
;
1141 case WAIT_EXECUTE_LIST
:
1142 if (MainController::get_state() == mctr::MC_READY
) {
1143 if (++executeListIndex
< mycfg
.execute_list_len
) {
1145 executeFromList(executeListIndex
);
1148 puts("Execution of [EXECUTE] section finished.");
1149 waitState
= WAIT_NOTHING
;
1158 int Cli::getHostIndex(const char* hostname
)
1160 int hostname_len
= strlen(hostname
);
1161 int index
, found
= -1;
1162 for (index
= 0; ; index
++) {
1163 const mctr::host_struct
*host
=
1164 MainController::get_host_data(index
);
1166 if (!strncmp(host
->hostname
, hostname
, hostname_len
) ||
1167 !strncmp(host
->hostname_local
, hostname
, hostname_len
)) {
1168 MainController::release_data();
1169 if (found
== -1) found
= index
;
1171 printf("Hostname %s is ambiguous.\n", hostname
);
1175 } else MainController::release_data();
1177 MainController::release_data();
1178 if (found
== -1) printf("No such host: %s.\n", hostname
);
1185 void Cli::executeFromList(int index
)
1187 if (index
>= mycfg
.execute_list_len
) {
1188 fputs("Cli::executeFromList: invalid argument", stderr
);
1191 if (mycfg
.execute_list
[index
].testcase_name
== NULL
) {
1192 MainController::execute_control(mycfg
.execute_list
[index
].module_name
);
1193 } else if (!strcmp(mycfg
.execute_list
[index
].testcase_name
, "*")) {
1194 MainController::execute_testcase(mycfg
.execute_list
[index
].module_name
,
1197 MainController::execute_testcase(mycfg
.execute_list
[index
].module_name
,
1198 mycfg
.execute_list
[index
].testcase_name
);
1202 //----------------------------------------------------------------------------
1205 // indent-tabs-mode: nil
1206 // c-basic-offset: 2
1209 extern boolean error_flag
; // in config_read.y
1210 extern std::string
get_cfg_read_current_file(); // in config_read.y
1211 extern int config_read_lineno
; // in config_read.l
1212 extern char *config_read_text
; // in config_read.l
1214 void config_read_warning(const char *warning_str
, ...)
1216 fprintf(stderr
, "Warning: in configuration file `%s', line %d: ",
1217 get_cfg_read_current_file().c_str(), config_read_lineno
);
1219 va_start(pvar
, warning_str
);
1220 vfprintf(stderr
, warning_str
, pvar
);
1225 void config_read_error(const char *error_str
, ...)
1227 fprintf(stderr
, "Parse error in configuration file `%s': in line %d, "
1228 "at or before token `%s': ",
1229 get_cfg_read_current_file().c_str(), config_read_lineno
, config_read_text
);
1231 va_start(pvar
, error_str
);
1232 vfprintf(stderr
, error_str
, pvar
);
1238 void config_preproc_error(const char *error_str
, ...)
1240 fprintf(stderr
, "Parse error while pre-processing configuration file "
1241 "`%s': in line %d: ",
1242 get_cfg_preproc_current_file().c_str(),
1243 config_preproc_yylineno
);
1245 va_start(pvar
, error_str
);
1246 vfprintf(stderr
, error_str
, pvar
);
1252 // called by functions in path.c
1253 void path_error(const char *fmt
, ...)
1255 fprintf(stderr
, "File error: ");
1257 va_start(parameters
, fmt
);
1258 vfprintf(stderr
, fmt
, parameters
);