debugger: added function breakpoints and added a setting for handling function call...
[deliverable/titan.core.git] / mctr2 / cli / Cli.cc
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
7 *
8 * Contributors:
9 * Balasko, Jeno
10 * Baranyi, Botond
11 * Bene, Tamas
12 * Beres, Szabolcs
13 * Delic, Adam
14 * Forstner, Matyas
15 * Gecse, Roland
16 * Kovacs, Ferenc
17 * Lovassy, Arpad
18 * Raduly, Csaba
19 * Szabados, Kristof
20 * Szabo, Janos Zoltan – initial implementation
21 * Szalai, Gabor
22 * Zalanyi, Balazs Andor
23 * Roland Gecse - author
24 *
25 ******************************************************************************/
26 //
27 // Description: Implementation file for Cli
28 //
29 //----------------------------------------------------------------------------
30 #include "Cli.h"
31 #include "../mctr/MainController.h"
32
33 #include <stdio.h>
34 #include "../editline/libedit/src/editline/readline.h"
35 #include <ctype.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <stdlib.h>
39 #include <pthread.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"
45
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"
63
64 using mctr::MainController;
65 using namespace cli;
66
67 /**
68 * shell_mode == TRUE while editing a command line that is passed to the shell
69 */
70 static boolean shell_mode;
71
72 struct Command {
73 const char *name;
74 callback_t callback_function;
75 const char *synopsis;
76 const char *description;
77 };
78
79 static const Command command_list[] = {
80 { CMTC_TEXT, &Cli::cmtcCallback, CMTC_TEXT " [hostname]",
81 "Create the MTC." },
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 "
89 "test case." },
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 }
108 };
109
110 struct DebugCommand {
111 const char *name;
112 int commandID;
113 const char *synopsis;
114 const char *description;
115 };
116
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 }
181 };
182
183 Cli::Cli()
184 {
185 loggingEnabled = TRUE;
186 exitFlag = FALSE;
187 waitState = WAIT_NOTHING;
188 executeListIndex = 0;
189
190 if (pthread_mutex_init(&mutex, NULL)) {
191 perror("Cli::Cli: pthread_mutex_init failed.");
192 exit(EXIT_FAILURE);
193 }
194 if (pthread_cond_init(&cond, NULL)) {
195 perror("Cli::Cli: pthread_cond_init failed.");
196 exit(EXIT_FAILURE);
197 }
198 }
199
200 Cli::~Cli()
201 {
202 pthread_mutex_destroy(&mutex);
203 pthread_cond_destroy(&cond);
204 }
205
206 //----------------------------------------------------------------------------
207 // MANDATORY
208
209 int Cli::enterLoop(int argc, char *argv[])
210 {
211 // Parameter check: mctr [config file name]
212 if (argc > 2) {
213 printUsage(argv[0]);
214 return EXIT_FAILURE;
215 }
216
217 printWelcome();
218
219 if (argc == 2) {
220 printf("Using configuration file: %s\n", argv[1]);
221 if (process_config_read_file(argv[1], &mycfg)) {
222 puts("Error was found in the configuration file. Exiting.");
223 cleanUp();
224 return EXIT_FAILURE;
225 }
226 else {
227 MainController::set_kill_timer(mycfg.kill_timer);
228
229 for (int i = 0; i < mycfg.group_list_len; ++i) {
230 MainController::add_host(mycfg.group_list[i].group_name,
231 mycfg.group_list[i].host_name);
232 }
233
234 for (int i = 0; i < mycfg.component_list_len; ++i) {
235 MainController::assign_component(mycfg.component_list[i].host_or_group,
236 mycfg.component_list[i].component);
237 }
238 }
239 }
240
241 int ret_val;
242 if (mycfg.num_hcs <= 0) ret_val = interactiveMode();
243 else ret_val = batchMode();
244
245 cleanUp();
246 return ret_val;
247 }
248
249 //----------------------------------------------------------------------------
250 // MANDATORY
251
252 void Cli::status_change()
253 {
254 lock();
255 if (waitState != WAIT_NOTHING && conditionHolds(waitState)) {
256 waitState = WAIT_NOTHING;
257 signal();
258 }
259 unlock();
260 }
261
262 //----------------------------------------------------------------------------
263 // MANDATORY
264
265 void Cli::error(int /*severity*/, const char *message)
266 {
267 printf("Error: %s\n", message);
268 fflush(stdout);
269 // TODO: Error handling based on the MC state where the error happened
270 }
271
272 //----------------------------------------------------------------------------
273 // MANDATORY
274
275 void Cli::notify(const struct timeval *timestamp, const char *source,
276 int /*severity*/, const char *message)
277 {
278 if (loggingEnabled)
279 {
280 switch(mycfg.tsformat){
281 case TSF_TIME: // handled together
282 case TSF_DATE_TIME:
283 {
284 time_t tv_sec = timestamp->tv_sec;
285 struct tm *lt = localtime(&tv_sec);
286 if (lt == NULL) {
287 printf("localtime() call failed.");
288 printf("%s: %s\n", source, message);
289 fflush(stdout);
290 break;
291 }
292 if (mycfg.tsformat == TSF_TIME) {
293 printf("%02d:%02d:%02d.%06ld %s: %s\n",
294 lt->tm_hour, lt->tm_min, lt->tm_sec, timestamp->tv_usec,source, message);
295 } else {
296 static const char * const month_names[] = { "Jan", "Feb", "Mar",
297 "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
298 printf("%4d/%s/%02d %02d:%02d:%02d.%06ld %s: %s\n",
299 lt->tm_year + 1900, month_names[lt->tm_mon],
300 lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec,
301 timestamp->tv_usec
302 ,source, message);
303 }
304 }
305 fflush(stdout);
306 break;
307 case TSF_SEC:
308 printf("%ld.%06ld %s: %s\n", (long)timestamp->tv_sec, timestamp->tv_usec, source, message);
309 fflush(stdout);
310
311 break;
312 default:
313 printf("%s: %s\n", source, message);
314 fflush(stdout);
315 break;
316 }
317 }
318 }
319
320 //----------------------------------------------------------------------------
321 // PRIVATE
322
323 void Cli::cleanUp()
324 {
325 }
326
327 //----------------------------------------------------------------------------
328 // PRIVATE
329
330 void Cli::printWelcome()
331 {
332 printf("\n"
333 "*************************************************************************\n"
334 "* TTCN-3 Test Executor - Main Controller 2 *\n"
335 "* Version: %-40s *\n"
336 "* Copyright (c) 2000-2016 Ericsson Telecom AB *\n"
337 "* All rights reserved. This program and the accompanying materials *\n"
338 "* are made available under the terms of the Eclipse Public License v1.0 *\n"
339 "* which accompanies this distribution, and is available at *\n"
340 "* http://www.eclipse.org/legal/epl-v10.html *\n"
341 "*************************************************************************\n"
342 "\n", PRODUCT_NUMBER);
343 }
344
345 void Cli::printUsage(const char *prg_name)
346 {
347 fprintf(stderr,
348 "TTCN-3 Test Executor - Main Controller 2\n"
349 "Version: " PRODUCT_NUMBER "\n\n"
350 "usage: %s [configuration_file]\n"
351 "where: the optional 'configuration_file' parameter specifies the name "
352 "and\nlocation of the main controller configuration file"
353 "\n", prg_name);
354 }
355
356 //----------------------------------------------------------------------------
357 // PRIVATE
358
359 int Cli::interactiveMode()
360 {
361 // Initialize history library
362 using_history();
363 // Tailor $HOME/...
364 const char *home_directory = getenv("HOME");
365 if(home_directory == NULL)
366 home_directory = ".";
367 char *ttcn3_history_filename = mprintf("%s/%s", home_directory,
368 TTCN3_HISTORY_FILENAME);
369 // Read history from file, don't bother if it does not exist!
370 read_history(ttcn3_history_filename);
371 // Set our own command completion function
372 rl_completion_entry_function = (Function*)completeCommand;
373 // Override rl_getc() in order to detect shell mode
374 rl_getc_function = getcWithShellDetection;
375
376 // TCP listen port parameter, returns TCP port on which it listens!
377 // Returns 0 on error.
378 if (MainController::start_session(mycfg.local_addr, mycfg.tcp_listen_port,
379 mycfg.unix_sockets_enabled) == 0) {
380 puts("Initialization of TCP server failed. Exiting.");
381 Free(ttcn3_history_filename);
382 return EXIT_FAILURE;
383 }
384
385 do {
386 char *line_read = readline(PROMPT);
387 if (line_read != NULL) {
388 shell_mode = FALSE;
389 stripLWS(line_read);
390 if (*line_read) {
391 add_history(line_read); // history maintains its own copy
392 // process and free line
393 processCommand(line_read);
394 free(line_read);
395 line_read = NULL;
396 }
397 } else {
398 // EOF was received
399 puts("exit");
400 exitCallback("");
401 }
402 } while (!exitFlag);
403
404 if (write_history(ttcn3_history_filename))
405 perror("Could not save history.");
406 Free(ttcn3_history_filename);
407
408 return EXIT_SUCCESS;
409 }
410
411 int Cli::batchMode()
412 {
413 printf("Entering batch mode. Waiting for %d HC%s to connect...\n",
414 mycfg.num_hcs, mycfg.num_hcs > 1 ? "s" : "");
415 if (mycfg.execute_list_len <= 0) {
416 puts("No [EXECUTE] section was given in the configuration file. "
417 "Exiting.");
418 return EXIT_FAILURE;
419 }
420 boolean error_flag = FALSE;
421 // start to listen on TCP port
422 if (MainController::start_session(mycfg.local_addr, mycfg.tcp_listen_port,
423 mycfg.unix_sockets_enabled) == 0) {
424 puts("Initialization of TCP server failed. Exiting.");
425 return EXIT_FAILURE;
426 }
427 waitMCState(WAIT_HC_CONNECTED);
428 // download config file
429 MainController::configure(mycfg.config_read_buffer);
430 waitMCState(WAIT_ACTIVE);
431 if (MainController::get_state() != mctr::MC_ACTIVE) {
432 puts("Error during initialization. Cannot continue in batch mode.");
433 error_flag = TRUE;
434 }
435 if (!error_flag) {
436 // create MTC on firstly connected HC
437 MainController::create_mtc(0);
438 waitMCState(WAIT_MTC_CREATED);
439 if (MainController::get_state() != mctr::MC_READY) {
440 puts("Creation of MTC failed. Cannot continue in batch mode.");
441 error_flag = TRUE;
442 }
443 }
444 if (!error_flag) {
445 // execute each item of the list
446 for (int i = 0; i < mycfg.execute_list_len; i++) {
447 executeFromList(i);
448 waitMCState(WAIT_MTC_READY);
449 if (MainController::get_state() != mctr::MC_READY) {
450 puts("MTC terminated unexpectedly. Cannot continue in batch "
451 "mode.");
452 error_flag = TRUE;
453 break;
454 }
455 }
456 }
457 if (!error_flag) {
458 // terminate the MTC
459 MainController::exit_mtc();
460 waitMCState(WAIT_MTC_TERMINATED);
461 }
462 // now MC must be in state MC_ACTIVE anyway
463 // shutdown MC
464 MainController::shutdown_session();
465 waitMCState(WAIT_SHUTDOWN_COMPLETE);
466 if (error_flag) return EXIT_FAILURE;
467 else return EXIT_SUCCESS;
468 }
469
470 //----------------------------------------------------------------------------
471 // PRIVATE
472
473 void Cli::processCommand(char *line_read)
474 {
475 for (const Command *command = command_list; command->name != NULL;
476 command++) {
477 size_t command_name_len = strlen(command->name);
478 if (!strncmp(line_read, command->name, command_name_len)) {
479 memset(line_read, ' ', command_name_len);
480 stripLWS(line_read);
481 (this->*command->callback_function)(line_read);
482 return;
483 }
484 }
485 for (const DebugCommand* command = debug_command_list; command->name != NULL;
486 command++) {
487 size_t command_name_len = strlen(command->name);
488 if (!strncmp(line_read, command->name, command_name_len)) {
489 memset(line_read, ' ', command_name_len);
490 stripLWS(line_read);
491 MainController::debug_command(command->commandID, line_read);
492 if (waitState == WAIT_EXECUTE_LIST && command->commandID == D_EXIT &&
493 !strcmp(line_read, "all")) {
494 // stop executing the list from the config file
495 waitState = WAIT_NOTHING;
496 }
497 return;
498 }
499 }
500 puts("Unknown command, try again...");
501 }
502
503 //----------------------------------------------------------------------------
504 // PRIVATE
505 // Create Main Test Component
506
507 void Cli::cmtcCallback(const char *arguments)
508 {
509 int hostIndex;
510 if(*arguments == 0) hostIndex = 0;
511 else {
512 hostIndex = getHostIndex(arguments);
513 if (hostIndex < 0) return;
514 }
515 switch (MainController::get_state()) {
516 case mctr::MC_LISTENING:
517 case mctr::MC_LISTENING_CONFIGURED:
518 puts("Waiting for HC to connect...");
519 waitMCState(WAIT_HC_CONNECTED);
520 case mctr::MC_HC_CONNECTED:
521 MainController::configure(mycfg.config_read_buffer);
522 waitMCState(WAIT_ACTIVE);
523 if (MainController::get_state() != mctr::MC_ACTIVE) {
524 puts("Error during initialization. Cannot create MTC.");
525 break;
526 }
527 case mctr::MC_ACTIVE:
528 MainController::create_mtc(hostIndex);
529 waitMCState(WAIT_MTC_CREATED);
530 break;
531 default:
532 puts("MTC already exists.");
533 }
534 fflush(stdout);
535 }
536
537 //----------------------------------------------------------------------------
538 // PRIVATE
539 // Start Main Test Component and execute the
540 // a) control part or testcase (or *) given in arguments
541 // b) EXECUTE part of supplied configuration file -- if arguments==NULL
542
543 void Cli::smtcCallback(const char *arguments)
544 {
545 switch (MainController::get_state()) {
546 case mctr::MC_LISTENING:
547 case mctr::MC_LISTENING_CONFIGURED:
548 case mctr::MC_HC_CONNECTED:
549 case mctr::MC_ACTIVE:
550 puts("MTC does not exist.");
551 break;
552 case mctr::MC_READY:
553 if (*arguments == 0) {
554 // Execute configuration file's execute section
555 if (mycfg.execute_list_len > 0) {
556 puts("Executing all items of [EXECUTE] section.");
557 waitState = WAIT_EXECUTE_LIST;
558 executeListIndex = 0;
559 executeFromList(0);
560 } else {
561 puts("No [EXECUTE] section was given in the configuration "
562 "file.");
563 }
564 } else { // Check the arguments
565 size_t doti = 0, alen = strlen(arguments), state = 0;
566 for (size_t r = 0; r < alen; r++) {
567 switch (arguments[r]) {
568 case '.':
569 ++state;
570 doti = r;
571 break;
572 case ' ':
573 case '\t':
574 state = 3;
575 }
576 }
577
578 if(state > 1) { // incorrect argument
579 puts("Incorrect argument format.");
580 helpCallback(SMTC_TEXT);
581 } else {
582 if(state == 0) { // only modulename is given in arguments
583 MainController::execute_control(arguments);
584 } else { // modulename.something in arguments
585 expstring_t arg_copy = mcopystr(arguments);
586 arg_copy[doti++] = '\0';
587 if (!strcmp(arg_copy + doti, "*"))
588 MainController::execute_testcase(arg_copy, NULL);
589 else if (!strcmp(arg_copy + doti, "control"))
590 MainController::execute_control(arg_copy);
591 else MainController::execute_testcase(arg_copy,
592 arg_copy + doti);
593 Free(arg_copy);
594 }
595 }
596 }
597 break;
598 default:
599 puts("MTC is busy.");
600 }
601 fflush(stdout);
602 }
603
604 //----------------------------------------------------------------------------
605 // PRIVATE
606 // Stops test execution
607
608 void Cli::stopCallback(const char *arguments)
609 {
610 if (*arguments == 0) {
611 switch (MainController::get_state()) {
612 case mctr::MC_TERMINATING_TESTCASE:
613 case mctr::MC_EXECUTING_CONTROL:
614 case mctr::MC_EXECUTING_TESTCASE:
615 case mctr::MC_PAUSED:
616 MainController::stop_execution();
617 if (waitState == WAIT_EXECUTE_LIST) waitState = WAIT_NOTHING;
618 break;
619 default:
620 puts("Tests are not running.");
621 }
622 } else helpCallback(STOP_TEXT);
623 }
624
625 //----------------------------------------------------------------------------
626 // PRIVATE
627 // Sets whether to interrupt test execution after testcase
628
629 void Cli::pauseCallback(const char *arguments)
630 {
631 if (arguments[0] != '\0') {
632 if (!strcmp(arguments, "on")) {
633 if (!MainController::get_stop_after_testcase()) {
634 MainController::stop_after_testcase(TRUE);
635 puts("Pause function is enabled. "
636 "Execution will stop at the end of each testcase.");
637 } else puts("Pause function is already enabled.");
638 } else if (!strcmp(arguments, "off")) {
639 if (MainController::get_stop_after_testcase()) {
640 MainController::stop_after_testcase(FALSE);
641 puts("Pause function is disabled. "
642 "Execution will continue at the end of each testcase.");
643 } else puts("Pause function is already disabled.");
644 } else helpCallback(PAUSE_TEXT);
645 } else printf("Pause function is %s.\n",
646 MainController::get_stop_after_testcase() ? "enabled" : "disabled");
647 }
648
649 //----------------------------------------------------------------------------
650 // PRIVATE
651 // Resumes interrupted test execution
652
653 void Cli::continueCallback(const char *arguments)
654 {
655 if (*arguments == 0) {
656 switch (MainController::get_state()) {
657 case mctr::MC_TERMINATING_TESTCASE:
658 case mctr::MC_EXECUTING_CONTROL:
659 case mctr::MC_EXECUTING_TESTCASE:
660 puts("Execution is not paused.");
661 break;
662 case mctr::MC_PAUSED:
663 MainController::continue_testcase();
664 break;
665 default:
666 puts("Tests are not running.");
667 }
668 } else helpCallback(CONTINUE_TEXT);
669 }
670
671 //----------------------------------------------------------------------------
672 // PRIVATE
673 // Exit Main Test Component
674
675 void Cli::emtcCallback(const char *arguments)
676 {
677 if(*arguments == 0) {
678 switch (MainController::get_state()) {
679 case mctr::MC_LISTENING:
680 case mctr::MC_LISTENING_CONFIGURED:
681 case mctr::MC_HC_CONNECTED:
682 case mctr::MC_ACTIVE:
683 puts("MTC does not exist.");
684 break;
685 case mctr::MC_READY:
686 MainController::exit_mtc();
687 waitMCState(WAIT_MTC_TERMINATED);
688 break;
689 default:
690 puts("MTC cannot be terminated.");
691 }
692 } else {
693 helpCallback(EMTC_TEXT);
694 }
695 }
696
697 //----------------------------------------------------------------------------
698 // PRIVATE
699 // Controls console logging
700
701 void Cli::logCallback(const char *arguments)
702 {
703 if (arguments[0] != '\0') {
704 if (!strcmp(arguments, "on")) {
705 loggingEnabled = TRUE;
706 puts("Console logging is enabled.");
707 } else if (!strcmp(arguments, "off")) {
708 loggingEnabled = FALSE;
709 puts("Console logging is disabled.");
710 } else helpCallback(LOG_TEXT);
711 } else printf("Console logging is %s.\n",
712 loggingEnabled ? "enabled" : "disabled");
713 }
714
715 //----------------------------------------------------------------------------
716 // PRIVATE
717 // Print connection information
718
719 void Cli::infoCallback(const char *arguments)
720 {
721 if (*arguments == 0) printInfo();
722 else helpCallback(INFO_TEXT);
723 }
724
725 //----------------------------------------------------------------------------
726 // PRIVATE
727 // Reconfigure MC and HCs
728
729 void Cli::reconfCallback(const char *arguments)
730 {
731 if(*arguments == 0) { // reconf called without its optional argument
732 puts("Reconfiguration of MC and HCs using original configuration "
733 "data\n -- not supported, yet.");
734 } else { // reconf called with config_file argument
735 puts("Reconfiguration of MC and HCs using configuration file"
736 "specified in\ncommand line argument -- not supported, yet.");
737 }
738 }
739
740 //----------------------------------------------------------------------------
741 // PRIVATE
742 // Print general help or help on command usage to console
743
744 void Cli::helpCallback(const char *arguments)
745 {
746 if (*arguments == 0) {
747 puts("Help is available for the following commands:");
748 for (const Command *command = command_list;
749 command->name != NULL; command++) {
750 printf("%s ", command->name);
751 }
752 for (const DebugCommand *command = debug_command_list;
753 command->name != NULL; command++) {
754 printf("%s ", command->name);
755 }
756 putchar('\n');
757 } else {
758 for (const Command *command = command_list;
759 command->name != NULL; command++) {
760 if (!strncmp(arguments, command->name,
761 strlen(command->name))) {
762 printf("%s usage: %s\n%s\n", command->name,
763 command->synopsis,
764 command->description);
765 return;
766 }
767 }
768 for (const DebugCommand *command = debug_command_list;
769 command->name != NULL; command++) {
770 if (!strncmp(arguments, command->name,
771 strlen(command->name))) {
772 printf("%s usage: %s\n%s\n", command->name,
773 command->synopsis,
774 command->description);
775 return;
776 }
777 }
778 printf("No help for %s.\n", arguments);
779 }
780 }
781
782 //----------------------------------------------------------------------------
783 // PRIVATE
784 // Pass command to shell
785
786 void Cli::shellCallback(const char *arguments)
787 {
788 if(system(arguments) == -1) {
789 perror("Error executing command in subshell.");
790 }
791 }
792
793 //----------------------------------------------------------------------------
794 // PRIVATE
795 // User initiated MC termination, exits the program.
796 // Save history into file and terminate CLI session.
797
798 void Cli::exitCallback(const char *arguments)
799 {
800 if (*arguments == 0) {
801 switch (MainController::get_state()) {
802 case mctr::MC_READY:
803 MainController::exit_mtc();
804 waitMCState(WAIT_MTC_TERMINATED);
805 case mctr::MC_LISTENING:
806 case mctr::MC_LISTENING_CONFIGURED:
807 case mctr::MC_HC_CONNECTED:
808 case mctr::MC_ACTIVE:
809 MainController::shutdown_session();
810 waitMCState(WAIT_SHUTDOWN_COMPLETE);
811 exitFlag = TRUE;
812 break;
813 default:
814 puts("Cannot exit until execution is finished.");
815 }
816 } else {
817 helpCallback(EXIT_TEXT);
818 }
819 }
820
821 void Cli::executeBatchFile(const char* filename)
822 {
823 FILE* fp = fopen(filename, "r");
824 if (fp == NULL) {
825 printf("Failed to open file '%s' for reading.\n", filename);
826 return;
827 }
828 else {
829 printf("Executing batch file '%s'.\n", filename);
830 }
831 char line[1024];
832 while (fgets(line, sizeof(line), fp) != NULL) {
833 size_t len = strlen(line);
834 if (line[len - 1] == '\n') {
835 line[len - 1] = '\0';
836 --len;
837 }
838 if (len != 0) {
839 printf("%s\n", line);
840 processCommand(line);
841 }
842 }
843 if (!feof(fp)) {
844 printf("Error occurred while reading batch file '%s' (error code: %d).\n",
845 filename, ferror(fp));
846 }
847 fclose(fp);
848 }
849
850 //----------------------------------------------------------------------------
851 // PRIVATE
852 // Command completion function implementation for readline() library.
853 // Heavily uses the ``static Command command_list[]'' array!
854
855 char *Cli::completeCommand(const char *prefix, int state)
856 {
857 static int command_index;
858 static int debug_command_index;
859 static size_t prefix_len;
860 const char *command_name;
861
862 if(shell_mode)
863 return rl_filename_completion_function(prefix, state);
864
865 if(state == 0) {
866 command_index = 0;
867 debug_command_index = 0;
868 prefix_len = strlen(prefix);
869 }
870
871 while((command_name = command_list[command_index].name)) {
872 ++command_index;
873 if(strncmp(prefix, command_name, prefix_len) == 0) {
874 // Must allocate buffer for returned string (readline frees it)
875 return strdup(command_name);
876 }
877 }
878
879 while ((command_name = debug_command_list[debug_command_index].name)) {
880 ++debug_command_index;
881 if (strncmp(prefix, command_name, prefix_len) == 0) {
882 // Must allocate buffer for returned string (readline frees it)
883 return strdup(command_name);
884 }
885 }
886 // No match found
887 return NULL;
888 }
889
890 //----------------------------------------------------------------------------
891 // PRIVATE
892 // Character input function implementation for readline() library.
893
894 int Cli::getcWithShellDetection(FILE *fp)
895 {
896 int input_char = getc(fp);
897 if(input_char == SHELL_ESCAPE)
898 shell_mode = TRUE;
899 return input_char;
900 }
901
902
903 //----------------------------------------------------------------------------
904 // PRIVATE
905
906 void Cli::stripLWS(char *input_text)
907 {
908 if(input_text == NULL) {
909 puts("stripLWS() called with NULL.");
910 exit(EXIT_FAILURE); // This shall never happen
911 }
912 size_t head_index, tail_index, input_len = strlen(input_text);
913 if(input_len < 1) return;
914 for(head_index = 0; isspace(input_text[head_index]); head_index++) ;
915 for(tail_index = input_len - 1; tail_index >= head_index &&
916 isspace(input_text[tail_index]); tail_index--) ;
917 size_t output_len = tail_index - head_index + 1;
918 memmove(input_text, input_text + head_index, output_len);
919 memset(input_text + output_len, 0, input_len - output_len);
920 }
921
922 const char *Cli::verdict2str(verdicttype verdict)
923 {
924 switch (verdict) {
925 case NONE:
926 return "none";
927 case PASS:
928 return "pass";
929 case INCONC:
930 return "inconc";
931 case FAIL:
932 return "fail";
933 case ERROR:
934 return "error";
935 default:
936 return "unknown";
937 }
938 }
939
940 //----------------------------------------------------------------------------
941 // PRIVATE
942
943 void Cli::printInfo()
944 {
945 puts("MC information:");
946 printf(" MC state: %s\n",
947 MainController::get_mc_state_name(MainController::get_state()));
948 puts(" host information:");
949 int host_index = 0;
950 for ( ; ; host_index++) {
951 mctr::host_struct *host = MainController::get_host_data(host_index);
952 if (host != NULL) {
953 printf(" - %s", host->hostname);
954 const char *ip_addr = host->ip_addr->get_addr_str();
955 // if the hostname differs from the IP address
956 // (i.e. the host has a DNS entry)
957 if (strcmp(ip_addr, host->hostname)) printf(" [%s]", ip_addr);
958 // if the local hostname differs from the prefix of the DNS name
959 if (strncmp(host->hostname, host->hostname_local,
960 strlen(host->hostname_local)))
961 printf(" (%s)", host->hostname_local);
962 puts(":");
963 printf(" operating system: %s %s on %s\n", host->system_name,
964 host->system_release, host->machine_type);
965 printf(" HC state: %s\n",
966 MainController::get_hc_state_name(host->hc_state));
967
968 puts(" test component information:");
969 // make a copy of the array containing component references
970 int n_components = host->n_components;
971 component *components = (component*)Malloc(n_components *
972 sizeof(component));
973 memcpy(components, host->components, n_components *
974 sizeof(component));
975 // the host structure has to be released in order to get access
976 // to the component structures
977 MainController::release_data();
978 for (int component_index = 0; component_index < n_components;
979 component_index++) {
980 mctr::component_struct *comp = MainController::
981 get_component_data(components[component_index]);
982 // if the component has a name
983 if (comp->comp_name != NULL)
984 printf(" - name: %s, component reference: %d\n",
985 comp->comp_name, comp->comp_ref);
986 else printf(" - component reference: %d\n",
987 comp->comp_ref);
988 if (comp->comp_type.definition_name != NULL) {
989 printf(" component type: ");
990 if (comp->comp_type.module_name != NULL)
991 printf("%s.", comp->comp_type.module_name);
992 printf("%s\n", comp->comp_type.definition_name);
993 }
994 printf(" state: %s\n",
995 MainController::get_tc_state_name(comp->tc_state));
996 if (comp->tc_fn_name.definition_name != NULL) {
997 printf(" executed %s: ",
998 comp->comp_ref == MTC_COMPREF ?
999 "test case" : "function");
1000 if (comp->tc_fn_name.module_name != NULL)
1001 printf("%s.", comp->tc_fn_name.module_name);
1002 printf("%s\n", comp->tc_fn_name.definition_name);
1003 }
1004 if (comp->tc_state == mctr::TC_EXITING ||
1005 comp->tc_state == mctr::TC_EXITED)
1006 printf(" local verdict: %s\n",
1007 verdict2str(comp->local_verdict));
1008 MainController::release_data();
1009 }
1010 if (n_components == 0) puts(" no components on this host");
1011 Free(components);
1012 } else {
1013 MainController::release_data();
1014 break;
1015 }
1016 }
1017 if (host_index == 0) puts(" no HCs are connected");
1018 printf(" pause function: %s\n", MainController::get_stop_after_testcase() ?
1019 "enabled" : "disabled");
1020 printf(" console logging: %s\n", loggingEnabled ?
1021 "enabled" : "disabled");
1022 fflush(stdout);
1023 }
1024
1025 //----------------------------------------------------------------------------
1026 // PRIVATE
1027
1028 void Cli::lock()
1029 {
1030 if (pthread_mutex_lock(&mutex)) {
1031 perror("Cli::lock: pthread_mutex_lock failed.");
1032 exit(EXIT_FAILURE);
1033 }
1034 }
1035
1036 void Cli::unlock()
1037 {
1038 if (pthread_mutex_unlock(&mutex)) {
1039 perror("Cli::unlock: pthread_mutex_unlock failed.");
1040 exit(EXIT_FAILURE);
1041 }
1042 }
1043
1044 void Cli::wait()
1045 {
1046 if (pthread_cond_wait(&cond, &mutex)) {
1047 perror("Cli::wait: pthread_cond_wait failed.");
1048 exit(EXIT_FAILURE);
1049 }
1050 }
1051
1052 void Cli::signal()
1053 {
1054 if (pthread_cond_signal(&cond)) {
1055 perror("Cli::signal: pthread_cond_signal failed.");
1056 exit(EXIT_FAILURE);
1057 }
1058 }
1059
1060 void Cli::waitMCState(waitStateEnum newWaitState)
1061 {
1062 lock();
1063 if (newWaitState != WAIT_NOTHING) {
1064 if (conditionHolds(newWaitState)) {
1065 waitState = WAIT_NOTHING;
1066 } else {
1067 waitState = newWaitState;
1068 wait();
1069 }
1070 } else {
1071 fputs("Cli::waitMCState: invalid argument", stderr);
1072 exit(EXIT_FAILURE);
1073 }
1074 unlock();
1075 }
1076
1077 boolean Cli::conditionHolds(waitStateEnum askedState)
1078 {
1079 switch (askedState) {
1080 case WAIT_HC_CONNECTED:
1081 if (MainController::get_state() == mctr::MC_HC_CONNECTED) {
1082 if (mycfg.num_hcs > 0) {
1083 return MainController::get_nof_hosts() >= mycfg.num_hcs;
1084 }
1085 else return TRUE;
1086 } else return FALSE;
1087 case WAIT_ACTIVE:
1088 switch (MainController::get_state()) {
1089 case mctr::MC_ACTIVE: // normal case
1090 case mctr::MC_HC_CONNECTED: // error happened with config file
1091 case mctr::MC_LISTENING: // even more strange situations
1092 return TRUE;
1093 default:
1094 return FALSE;
1095 }
1096 case WAIT_MTC_CREATED:
1097 case WAIT_MTC_READY:
1098 switch (MainController::get_state()) {
1099 case mctr::MC_READY: // normal case
1100 case mctr::MC_ACTIVE: // MTC crashed unexpectedly
1101 case mctr::MC_LISTENING_CONFIGURED: // MTC and all HCs are crashed
1102 // at the same time
1103 case mctr::MC_HC_CONNECTED: // even more strange situations
1104 return TRUE;
1105 default:
1106 return FALSE;
1107 }
1108 case WAIT_MTC_TERMINATED:
1109 return MainController::get_state() == mctr::MC_ACTIVE;
1110 case WAIT_SHUTDOWN_COMPLETE:
1111 return MainController::get_state() == mctr::MC_INACTIVE;
1112 case WAIT_EXECUTE_LIST:
1113 if (MainController::get_state() == mctr::MC_READY) {
1114 if (++executeListIndex < mycfg.execute_list_len) {
1115 unlock();
1116 executeFromList(executeListIndex);
1117 lock();
1118 } else {
1119 puts("Execution of [EXECUTE] section finished.");
1120 waitState = WAIT_NOTHING;
1121 }
1122 }
1123 return FALSE;
1124 default:
1125 return FALSE;
1126 }
1127 }
1128
1129 int Cli::getHostIndex(const char* hostname)
1130 {
1131 int hostname_len = strlen(hostname);
1132 int index, found = -1;
1133 for (index = 0; ; index++) {
1134 const mctr::host_struct *host =
1135 MainController::get_host_data(index);
1136 if (host != NULL) {
1137 if (!strncmp(host->hostname, hostname, hostname_len) ||
1138 !strncmp(host->hostname_local, hostname, hostname_len)) {
1139 MainController::release_data();
1140 if (found == -1) found = index;
1141 else {
1142 printf("Hostname %s is ambiguous.\n", hostname);
1143 found = -1;
1144 break;
1145 }
1146 } else MainController::release_data();
1147 } else {
1148 MainController::release_data();
1149 if (found == -1) printf("No such host: %s.\n", hostname);
1150 break;
1151 }
1152 }
1153 return found;
1154 }
1155
1156 void Cli::executeFromList(int index)
1157 {
1158 if (index >= mycfg.execute_list_len) {
1159 fputs("Cli::executeFromList: invalid argument", stderr);
1160 exit(EXIT_FAILURE);
1161 }
1162 if (mycfg.execute_list[index].testcase_name == NULL) {
1163 MainController::execute_control(mycfg.execute_list[index].module_name);
1164 } else if (!strcmp(mycfg.execute_list[index].testcase_name, "*")) {
1165 MainController::execute_testcase(mycfg.execute_list[index].module_name,
1166 NULL);
1167 } else {
1168 MainController::execute_testcase(mycfg.execute_list[index].module_name,
1169 mycfg.execute_list[index].testcase_name);
1170 }
1171 }
1172
1173 //----------------------------------------------------------------------------
1174 // Local Variables:
1175 // mode: C++
1176 // indent-tabs-mode: nil
1177 // c-basic-offset: 2
1178 // End:
1179
1180 extern boolean error_flag; // in config_read.y
1181 extern std::string get_cfg_read_current_file(); // in config_read.y
1182 extern int config_read_lineno; // in config_read.l
1183 extern char *config_read_text; // in config_read.l
1184
1185 void config_read_warning(const char *warning_str, ...)
1186 {
1187 fprintf(stderr, "Warning: in configuration file `%s', line %d: ",
1188 get_cfg_read_current_file().c_str(), config_read_lineno);
1189 va_list pvar;
1190 va_start(pvar, warning_str);
1191 vfprintf(stderr, warning_str, pvar);
1192 va_end(pvar);
1193 putc('\n', stderr);
1194 }
1195
1196 void config_read_error(const char *error_str, ...)
1197 {
1198 fprintf(stderr, "Parse error in configuration file `%s': in line %d, "
1199 "at or before token `%s': ",
1200 get_cfg_read_current_file().c_str(), config_read_lineno, config_read_text);
1201 va_list pvar;
1202 va_start(pvar, error_str);
1203 vfprintf(stderr, error_str, pvar);
1204 va_end(pvar);
1205 putc('\n', stderr);
1206 error_flag = TRUE;
1207 }
1208
1209 void config_preproc_error(const char *error_str, ...)
1210 {
1211 fprintf(stderr, "Parse error while pre-processing configuration file "
1212 "`%s': in line %d: ",
1213 get_cfg_preproc_current_file().c_str(),
1214 config_preproc_yylineno);
1215 va_list pvar;
1216 va_start(pvar, error_str);
1217 vfprintf(stderr, error_str, pvar);
1218 va_end(pvar);
1219 putc('\n', stderr);
1220 error_flag = TRUE;
1221 }
1222
1223 // called by functions in path.c
1224 void path_error(const char *fmt, ...)
1225 {
1226 fprintf(stderr, "File error: ");
1227 va_list parameters;
1228 va_start(parameters, fmt);
1229 vfprintf(stderr, fmt, parameters);
1230 va_end(parameters);
1231 putc('\n', stderr);
1232 }
This page took 0.062257 seconds and 6 git commands to generate.