implemented the Main Controller's 'reconf' command (artf468488)
[deliverable/titan.core.git] / mctr2 / cli / Cli.cc
index 101930f986bd62bb8d40bce9ea5a106559ba562c..6a4886ab98e9fc36d5011a83edfd96005cda3e83 100644 (file)
@@ -1,16 +1,30 @@
-///////////////////////////////////////////////////////////////////////////////
-// Copyright (c) 2000-2015 Ericsson Telecom AB
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// which accompanies this distribution, and is available at
-// http://www.eclipse.org/legal/epl-v10.html
-///////////////////////////////////////////////////////////////////////////////
+/******************************************************************************
+ * Copyright (c) 2000-2016 Ericsson Telecom AB
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Balasko, Jeno
+ *   Baranyi, Botond
+ *   Bene, Tamas
+ *   Beres, Szabolcs
+ *   Delic, Adam
+ *   Forstner, Matyas
+ *   Gecse, Roland
+ *   Kovacs, Ferenc
+ *   Lovassy, Arpad
+ *   Raduly, Csaba
+ *   Szabados, Kristof
+ *   Szabo, Janos Zoltan – initial implementation
+ *   Szalai, Gabor
+ *   Zalanyi, Balazs Andor
+ *   Roland Gecse - author
+ *
+ ******************************************************************************/
 //
 // Description:           Implementation file for Cli
-// Author:                Gecse Roland
-// mail:                  ethrge@eth.ericsson.se
-//
-// Copyright (c) 2000-2015 Ericsson Telecom AB
 //
 //----------------------------------------------------------------------------
 #include "Cli.h"
@@ -27,6 +41,7 @@
 #include "../../common/version_internal.h"
 #include "../../common/memory.h"
 #include "../../common/config_preproc.h"
+#include "../../core/DebugCommands.hh"
 
 #define PROMPT "MC2> "
 #define CMTC_TEXT "cmtc"
@@ -42,6 +57,7 @@
 #define SHELL_TEXT "!"
 #define EXIT_TEXT "quit"
 #define EXIT_TEXT2 "exit"
+#define BATCH_TEXT "batch"
 #define SHELL_ESCAPE '!'
 #define TTCN3_HISTORY_FILENAME ".ttcn3_history"
 
@@ -86,9 +102,84 @@ static const Command command_list[] = {
     "Execute commands in subshell." },
   { EXIT_TEXT, &Cli::exitCallback, EXIT_TEXT, "Exit Main Controller." },
   { EXIT_TEXT2, &Cli::exitCallback, EXIT_TEXT2, "Exit Main Controller." },
+  { BATCH_TEXT, &Cli::executeBatchFile, BATCH_TEXT " <batch_file>",
+    "Run commands from batch file." },
   { NULL, NULL, NULL, NULL }
 };
 
+struct DebugCommand {
+  const char *name;
+  int commandID;
+  const char *synopsis;
+  const char *description;
+};
+
+static const DebugCommand debug_command_list[] = {
+  { D_SWITCH_TEXT, D_SWITCH, D_SWITCH_TEXT " on|off",
+    "Switch the debugger on or off." },
+  { D_SET_BREAKPOINT_TEXT, D_SET_BREAKPOINT,
+    D_SET_BREAKPOINT_TEXT " <module> <line>|<function> [<batch_file>]",
+    "Add a breakpoint at the specified location, or change the batch file of "
+    "an existing breakpoint." },
+  { D_REMOVE_BREAKPOINT_TEXT, D_REMOVE_BREAKPOINT,
+    D_REMOVE_BREAKPOINT_TEXT " all|<module> [all|<line>|<function>]",
+    "Remove a breakpoint, or all breakpoints from a module, or all breakpoints "
+    "from all modules." },
+  { D_SET_AUTOMATIC_BREAKPOINT_TEXT, D_SET_AUTOMATIC_BREAKPOINT,
+    D_SET_AUTOMATIC_BREAKPOINT_TEXT " error|fail on|off [<batch_file>]",
+    "Switch an automatic breakpoint (truggered by an event) on or off, and/or "
+    "change its batch file." },
+  { D_SET_OUTPUT_TEXT, D_SET_OUTPUT,
+    D_SET_OUTPUT_TEXT " console|file|both [file_name]",
+    "Set the output of the debugger." },
+  { D_SET_GLOBAL_BATCH_FILE_TEXT, D_SET_GLOBAL_BATCH_FILE,
+    D_SET_GLOBAL_BATCH_FILE_TEXT " on|off [batch_file_name]",
+    "Set whether a batch file should be executed automatically when test execution "
+    "is halted (breakpoint-specific batch files override this setting)." },
+  { D_FUNCTION_CALL_CONFIG_TEXT, D_FUNCTION_CALL_CONFIG,
+    D_FUNCTION_CALL_CONFIG_TEXT " file|<limit>|all [<file_name>]",
+    "Configure the storing of function call data." },
+  { D_PRINT_SETTINGS_TEXT, D_PRINT_SETTINGS, D_PRINT_SETTINGS_TEXT,
+    "Prints the debugger's settings." },
+  { D_LIST_COMPONENTS_TEXT, D_LIST_COMPONENTS, D_LIST_COMPONENTS_TEXT,
+    "List the test components currently running debuggable code." },
+  { D_SET_COMPONENT_TEXT, D_SET_COMPONENT,
+    D_SET_COMPONENT_TEXT " <component name>|<component_reference>",
+    "Set the test component to print debug information from." },
+  { D_PRINT_CALL_STACK_TEXT, D_PRINT_CALL_STACK, D_PRINT_CALL_STACK_TEXT,
+    "Print call stack." },
+  { D_SET_STACK_LEVEL_TEXT, D_SET_STACK_LEVEL, D_SET_STACK_LEVEL_TEXT " <level>",
+    "Set the stack level to print debug information from." },
+  { D_LIST_VARIABLES_TEXT, D_LIST_VARIABLES,
+    D_LIST_VARIABLES_TEXT " [local|global|comp|all] [pattern]",
+    "List variable names." },
+  { D_PRINT_VARIABLE_TEXT, D_PRINT_VARIABLE,
+    D_PRINT_VARIABLE_TEXT " <variable_name>|$ [{ <variable_name>|$}]",
+    "Print current value of one or more variables ('$' is substituted with the "
+    "result of the last " D_LIST_VARIABLES_TEXT " command)." },
+  { D_OVERWRITE_VARIABLE_TEXT, D_OVERWRITE_VARIABLE,
+    D_OVERWRITE_VARIABLE_TEXT " <variable_name> <value>",
+    "Overwrite the current value of a variable." },
+  { D_PRINT_FUNCTION_CALLS_TEXT, D_PRINT_FUNCTION_CALLS,
+    D_PRINT_FUNCTION_CALLS_TEXT " [all|<amount>]",
+    "Print function call data." },
+  { D_STEP_OVER_TEXT, D_STEP_OVER, D_STEP_OVER_TEXT,
+    "Resume test execution until the next line of code (in this function or the "
+    "caller function)." },
+  { D_STEP_INTO_TEXT, D_STEP_INTO, D_STEP_INTO_TEXT,
+    "Resume test execution until the next line of code (on any stack level)." },
+  { D_STEP_OUT_TEXT, D_STEP_OUT, D_STEP_OUT_TEXT,
+    "Resume test execution until the next line of code in the caller function." },
+  { D_RUN_TO_CURSOR_TEXT, D_RUN_TO_CURSOR,
+    D_RUN_TO_CURSOR_TEXT " <module> <line>|<function>",
+    "Resume test execution until the specified location." },
+  { D_HALT_TEXT, D_HALT, D_HALT_TEXT, "Halt test execution." },
+  { D_CONTINUE_TEXT, D_CONTINUE, D_CONTINUE_TEXT, "Resume halted test execution." },
+  { D_EXIT_TEXT, D_EXIT, D_EXIT_TEXT " test|all",
+    "Exit the current test or the execution of all tests." },
+  { NULL, D_ERROR, NULL, NULL }
+};
+
 Cli::Cli()
 {
   loggingEnabled = TRUE;
@@ -104,12 +195,14 @@ Cli::Cli()
     perror("Cli::Cli: pthread_cond_init failed.");
     exit(EXIT_FAILURE);
   }
+  cfg_file_name = NULL;
 }
 
 Cli::~Cli()
 {
   pthread_mutex_destroy(&mutex);
   pthread_cond_destroy(&cond);
+  Free(cfg_file_name);
 }
 
 //----------------------------------------------------------------------------
@@ -126,8 +219,9 @@ int Cli::enterLoop(int argc, char *argv[])
   printWelcome();
 
   if (argc == 2) {
-    printf("Using configuration file: %s\n", argv[1]);
-    if (process_config_read_file(argv[1], &mycfg)) {
+    cfg_file_name = mcopystr(argv[1]);
+    printf("Using configuration file: %s\n", cfg_file_name);
+    if (process_config_read_file(cfg_file_name, &mycfg)) {
       puts("Error was found in the configuration file. Exiting.");
       cleanUp();
       return EXIT_FAILURE;
@@ -242,7 +336,7 @@ void Cli::printWelcome()
     "*************************************************************************\n"
     "* TTCN-3 Test Executor - Main Controller 2                              *\n"
     "* Version: %-40s                     *\n"
-    "* Copyright (c) 2000-2015 Ericsson Telecom AB                           *\n"
+    "* Copyright (c) 2000-2016 Ericsson Telecom AB                           *\n"
     "* All rights reserved. This program and the accompanying materials      *\n"
     "* are made available under the terms of the Eclipse Public License v1.0 *\n"
     "* which accompanies this distribution, and is available at              *\n"
@@ -382,7 +476,7 @@ int Cli::batchMode()
 void Cli::processCommand(char *line_read)
 {
   for (const Command *command = command_list; command->name != NULL;
-    command++) {
+       command++) {
     size_t command_name_len = strlen(command->name);
     if (!strncmp(line_read, command->name, command_name_len)) {
       memset(line_read, ' ', command_name_len);
@@ -391,6 +485,21 @@ void Cli::processCommand(char *line_read)
       return;
     }
   }
+  for (const DebugCommand* command = debug_command_list; command->name != NULL;
+       command++) {
+    size_t command_name_len = strlen(command->name);
+    if (!strncmp(line_read, command->name, command_name_len)) {
+      memset(line_read, ' ', command_name_len);
+      stripLWS(line_read);
+      MainController::debug_command(command->commandID, line_read);
+      if (waitState == WAIT_EXECUTE_LIST && command->commandID == D_EXIT &&
+          !strcmp(line_read, "all")) {
+        // stop executing the list from the config file
+        waitState = WAIT_NOTHING;
+      }
+      return;
+    }
+  }
   puts("Unknown command, try again...");
 }
 
@@ -622,12 +731,37 @@ void Cli::infoCallback(const char *arguments)
 
 void Cli::reconfCallback(const char *arguments)
 {
-  if(*arguments == 0) { // reconf called without its optional argument
-    puts("Reconfiguration of MC and HCs using original configuration "
-      "data\n -- not supported, yet.");
-  } else { // reconf called with config_file argument
-    puts("Reconfiguration of MC and HCs using configuration file"
-      "specified in\ncommand line argument -- not supported, yet.");
+  if (!MainController::start_reconfiguring()) {
+    return;
+  }
+  if (*arguments != 0) {
+    Free(cfg_file_name);
+    cfg_file_name = mcopystr(arguments);
+  }
+  
+  printf("Using configuration file: %s\n", cfg_file_name);
+  if (process_config_read_file(cfg_file_name, &mycfg)) {
+    puts("Error was found in the configuration file. Exiting.");
+    cleanUp();
+    puts("exit");
+    exitCallback("");
+  }
+  else {
+    MainController::set_kill_timer(mycfg.kill_timer);
+
+    for (int i = 0; i < mycfg.group_list_len; ++i) {
+      MainController::add_host(mycfg.group_list[i].group_name,
+        mycfg.group_list[i].host_name);
+    }
+
+    for (int i = 0; i < mycfg.component_list_len; ++i) {
+      MainController::assign_component(mycfg.component_list[i].host_or_group,
+        mycfg.component_list[i].component);
+    }
+    
+    if (MainController::get_state() == mctr::MC_RECONFIGURING) {
+      MainController::configure(mycfg.config_read_buffer);
+    }
   }
 }
 
@@ -641,7 +775,11 @@ void Cli::helpCallback(const char *arguments)
     puts("Help is available for the following commands:");
     for (const Command *command = command_list;
       command->name != NULL; command++) {
-      printf(" %s", command->name);
+      printf("%s ", command->name);
+    }
+    for (const DebugCommand *command = debug_command_list;
+         command->name != NULL; command++) {
+      printf("%s ", command->name);
     }
     putchar('\n');
   } else {
@@ -655,6 +793,16 @@ void Cli::helpCallback(const char *arguments)
         return;
       }
     }
+    for (const DebugCommand *command = debug_command_list;
+         command->name != NULL; command++) {
+      if (!strncmp(arguments, command->name,
+        strlen(command->name))) {
+        printf("%s usage: %s\n%s\n", command->name,
+          command->synopsis,
+          command->description);
+        return;
+      }
+    }
     printf("No help for %s.\n", arguments);
   }
 }
@@ -680,6 +828,7 @@ void Cli::exitCallback(const char *arguments)
   if (*arguments == 0) {
     switch (MainController::get_state()) {
     case mctr::MC_READY:
+    case mctr::MC_RECONFIGURING:
       MainController::exit_mtc();
       waitMCState(WAIT_MTC_TERMINATED);
     case mctr::MC_LISTENING:
@@ -698,6 +847,35 @@ void Cli::exitCallback(const char *arguments)
   }
 }
 
+void Cli::executeBatchFile(const char* filename)
+{
+  FILE* fp = fopen(filename, "r");
+  if (fp == NULL) {
+    printf("Failed to open file '%s' for reading.\n", filename);
+    return;
+  }
+  else {
+    printf("Executing batch file '%s'.\n", filename);
+  }
+  char line[1024];
+  while (fgets(line, sizeof(line), fp) != NULL) {
+    size_t len = strlen(line);
+    if (line[len - 1] == '\n') {
+      line[len - 1] = '\0';
+      --len;
+    }
+    if (len != 0) {
+      printf("%s\n", line);
+      processCommand(line);
+    }
+  }
+  if (!feof(fp)) {
+    printf("Error occurred while reading batch file '%s' (error code: %d).\n",
+      filename, ferror(fp));
+  }
+  fclose(fp);
+}
+
 //----------------------------------------------------------------------------
 // PRIVATE
 // Command completion function implementation for readline() library.
@@ -706,6 +884,7 @@ void Cli::exitCallback(const char *arguments)
 char *Cli::completeCommand(const char *prefix, int state)
 {
   static int command_index;
+  static int debug_command_index;
   static size_t prefix_len;
   const char *command_name;
 
@@ -714,6 +893,7 @@ char *Cli::completeCommand(const char *prefix, int state)
 
   if(state == 0) {
     command_index = 0;
+    debug_command_index = 0;
     prefix_len = strlen(prefix);
   }
 
@@ -724,6 +904,14 @@ char *Cli::completeCommand(const char *prefix, int state)
       return strdup(command_name);
     }
   }
+  
+  while ((command_name = debug_command_list[debug_command_index].name)) {
+    ++debug_command_index;
+    if (strncmp(prefix, command_name, prefix_len) == 0) {
+      // Must allocate buffer for returned string (readline frees it)
+      return strdup(command_name);
+    }
+  }
   // No match found
   return NULL;
 }
This page took 0.029025 seconds and 5 git commands to generate.