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