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