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