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