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