("app,a", po::value<std::string>(), "application library name (default: icinga)")
("library,l", po::value<std::vector<std::string> >(), "load a library")
("include,I", po::value<std::vector<std::string> >(), "add include search directory")
- ("log-level,x", po::value<std::string>()
- , "specify the log level for the console log.\n"
- "The valid value is either debug, notice, information (default), warning, or critical");
+ ("log-level,x", po::value<std::string>(), "specify the log level for the console log.\n"
+ "The valid value is either debug, notice, information (default), warning, or critical")
+ ("script-debugger,X", "whether to enable the script debugger");
po::options_description hiddenDesc("Hidden options");
}
}
+ if (vm.count("script-debugger"))
+ Application::SetScriptDebuggerEnabled(true);
+
Application::DeclareStatePath(Application::GetLocalStateDir() + "/lib/icinga2/icinga2.state");
Application::DeclareModAttrPath(Application::GetLocalStateDir() + "/lib/icinga2/modified-attributes.conf");
Application::DeclareObjectsPath(Application::GetLocalStateDir() + "/cache/icinga2/icinga2.debug");
int Application::m_ArgC;
char **Application::m_ArgV;
double Application::m_StartTime;
+bool Application::m_ScriptDebuggerEnabled = false;
/**
* Constructor for the Application class.
m_StartTime = ts;
}
+bool Application::GetScriptDebuggerEnabled(void)
+{
+ return m_ScriptDebuggerEnabled;
+}
+
+void Application::SetScriptDebuggerEnabled(bool enabled)
+{
+ m_ScriptDebuggerEnabled = enabled;
+}
+
void Application::ValidateName(const String& value, const ValidationUtils& utils)
{
ObjectImpl<Application>::ValidateName(value, utils);
static double GetStartTime(void);
static void SetStartTime(double ts);
+ static bool GetScriptDebuggerEnabled(void);
+ static void SetScriptDebuggerEnabled(bool enabled);
+
static void DisplayInfoMessage(std::ostream& os, bool skipVersion = false);
protected:
static bool m_Debugging; /**< Whether debugging is enabled. */
static LogSeverity m_DebuggingSeverity; /**< Whether debugging severity is set. */
static double m_StartTime;
+ static bool m_ScriptDebuggerEnabled;
#ifndef _WIN32
static void SigIntTermHandler(int signum);
{ }
ScriptError::ScriptError(const String& message, const DebugInfo& di, bool incompleteExpr)
- : m_Message(message), m_DebugInfo(di), m_IncompleteExpr(incompleteExpr)
+ : m_Message(message), m_DebugInfo(di), m_IncompleteExpr(incompleteExpr), m_HandledByDebugger(false)
{ }
ScriptError::~ScriptError(void) throw()
return m_IncompleteExpr;;
}
+bool ScriptError::IsHandledByDebugger(void) const
+{
+ return m_HandledByDebugger;
+}
+
+void ScriptError::SetHandledByDebugger(bool handled)
+{
+ m_HandledByDebugger = handled;
+}
+
posix_error::posix_error(void)
: m_Message(NULL)
{ }
DebugInfo GetDebugInfo(void) const;
bool IsIncompleteExpression(void) const;
+ bool IsHandledByDebugger(void) const;
+ void SetHandledByDebugger(bool handled);
+
private:
String m_Message;
DebugInfo m_DebugInfo;
bool m_IncompleteExpr;
+ bool m_HandledByDebugger;
};
/*
REGISTER_CLICOMMAND("console", ConsoleCommand);
+INITIALIZE_ONCE(&ConsoleCommand::StaticInitialize);
+
+void ConsoleCommand::BreakpointHandler(ScriptFrame& frame, ScriptError *ex, const DebugInfo& di)
+{
+ static boost::mutex mutex;
+ boost::mutex::scoped_lock lock(mutex);
+
+ if (!Application::GetScriptDebuggerEnabled())
+ return;
+
+ if (ex && ex->IsHandledByDebugger())
+ return;
+
+ std::cout << "Breakpoint encountered in '" << di.Path << "' at " << di << "\n";
+
+ if (ex) {
+ std::cout << "Exception: " << DiagnosticInformation(*ex);
+ ex->SetHandledByDebugger(true);
+ }
+
+ std::cout << "You can leave the debugger and continue the program with \"$quit\".\n";
+
+ ConsoleCommand::RunScriptConsole(frame);
+}
+
+void ConsoleCommand::StaticInitialize(void)
+{
+ Expression::OnBreakpoint.connect(&ConsoleCommand::BreakpointHandler);
+}
+
String ConsoleCommand::GetDescription(void) const
{
return "Interprets Icinga script expressions.";
*/
int ConsoleCommand::Run(const po::variables_map& vm, const std::vector<std::string>& ap) const
{
- std::map<String, String> lines;
- int next_line = 1;
-
#ifdef HAVE_EDITLINE
rl_completion_entry_function = ConsoleCommand::ConsoleCompleteHelper;
rl_completion_append_character = '\0';
#endif /* HAVE_EDITLINE */
- String addr;
+ String addr, session;
ScriptFrame scriptFrame;
- l_ScriptFrame = &scriptFrame;
- l_Session = Utility::NewUniqueID();
+ session = Utility::NewUniqueID();
if (vm.count("sandbox"))
scriptFrame.Sandboxed = true;
if (addrEnv)
addr = addrEnv;
- if (vm.count("connect")) {
+ if (vm.count("connect"))
addr = vm["connect"].as<std::string>();
- }
+
+ String command;
+
+ if (vm.count("eval"))
+ command = vm["eval"].as<std::string>();
+
+ return RunScriptConsole(scriptFrame, addr, session, command);;
+}
+
+int ConsoleCommand::RunScriptConsole(ScriptFrame& scriptFrame, const String& addr, const String& session, const String& commandOnce)
+{
+ std::map<String, String> lines;
+ int next_line = 1;
+
+ l_ScriptFrame = &scriptFrame;
+ l_Session = session;
if (!addr.IsEmpty()) {
Url::Ptr url;
incomplete:
std::string line;
- if (!vm.count("eval")) {
+ if (commandOnce.IsEmpty()) {
#ifdef HAVE_EDITLINE
std::ostringstream promptbuf;
std::ostream& os = promptbuf;
std::getline(std::cin, line);
#endif /* HAVE_EDITLINE */
} else
- line = vm["eval"].as<std::string>();
+ line = commandOnce;
+
+ if (!line.empty() && line[0] == '$') {
+ if (line == "$quit")
+ break;
+
+ std::cout << "Unknown debugger command: " << line;
+ }
if (!command.empty())
command += "\n";
boost::rethrow_exception(eptr);
}
- if (!vm.count("eval")) {
+ if (commandOnce.IsEmpty()) {
std::cout << ConsoleColorTag(Console_ForegroundCyan);
ConfigWriter::EmitValue(std::cout, 1, result);
std::cout << ConsoleColorTag(Console_Normal) << "\n";
std::cout << ex.what() << "\n";
- if (vm.count("eval"))
+ if (!commandOnce.IsEmpty())
return EXIT_FAILURE;
} catch (const std::exception& ex) {
std::cout << "Error: " << DiagnosticInformation(ex) << "\n";
- if (vm.count("eval"))
+ if (!commandOnce.IsEmpty())
return EXIT_FAILURE;
}
#include "cli/clicommand.hpp"
#include "base/exception.hpp"
+#include "base/scriptframe.hpp"
namespace icinga
{
public:
DECLARE_PTR_TYPEDEFS(ConsoleCommand);
+ static void StaticInitialize(void);
+
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual ImpersonationLevel GetImpersonationLevel(void) const override;
boost::program_options::options_description& hiddenDesc) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
+ static int RunScriptConsole(ScriptFrame& scriptFrame, const String& addr = String(),
+ const String& session = String(), const String& commandOnce = String());
+
private:
mutable boost::mutex m_Mutex;
mutable boost::condition_variable m_CV;
static char *ConsoleCompleteHelper(const char *word, int state);
#endif /* HAVE_EDITLINE */
+ static void BreakpointHandler(ScriptFrame& frame, ScriptError *ex, const DebugInfo& di);
+
};
}
ignore_on_error return T_IGNORE_ON_ERROR;
current_filename return T_CURRENT_FILENAME;
current_line return T_CURRENT_LINE;
+debugger return T_DEBUGGER;
=\> return T_FOLLOWS;
\<\< return T_SHIFT_LEFT;
\>\> return T_SHIFT_RIGHT;
%token T_IGNORE_ON_ERROR "ignore_on_error (T_IGNORE_ON_ERROR)"
%token T_CURRENT_FILENAME "current_filename (T_CURRENT_FILENAME)"
%token T_CURRENT_LINE "current_line (T_CURRENT_LINE)"
+%token T_DEBUGGER "debugger (T_DEBUGGER)"
%token T_USE "use (T_USE)"
%token T_OBJECT "object (T_OBJECT)"
%token T_TEMPLATE "template (T_TEMPLATE)"
{
$$ = new ContinueExpression(@$);
}
+ | T_DEBUGGER
+ {
+ $$ = new BreakpointExpression(@$);
+ }
| apply
| object
| T_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')' rterm_scope_require_side_effect
using namespace icinga;
+boost::signals2::signal<void (ScriptFrame&, ScriptError *ex, const DebugInfo&)> Expression::OnBreakpoint;
+boost::thread_specific_ptr<bool> l_InBreakpointHandler;
+
Expression::~Expression(void)
{ }
+void Expression::ScriptBreakpoint(ScriptFrame& frame, ScriptError *ex, const DebugInfo& di)
+{
+ bool *inHandler = l_InBreakpointHandler.get();
+ if (!inHandler || !*inHandler) {
+ inHandler = new bool(true);
+ l_InBreakpointHandler.reset(inHandler);
+ OnBreakpoint(frame, ex, di);
+ *inHandler = false;
+ }
+}
+
ExpressionResult Expression::Evaluate(ScriptFrame& frame, DebugHint *dhint) const
{
try {
#endif /* I2_DEBUG */
return DoEvaluate(frame, dhint);
- } catch (const ScriptError& ex) {
+ } catch (ScriptError& ex) {
+ ScriptBreakpoint(frame, &ex, GetDebugInfo());
throw;
} catch (const std::exception& ex) {
BOOST_THROW_EXCEPTION(ScriptError("Error while evaluating expression: " + String(ex.what()), GetDebugInfo())
return res;
}
+
+ExpressionResult BreakpointExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+ ScriptBreakpoint(frame, NULL, GetDebugInfo());
+
+ return Empty;
+}
+
virtual const DebugInfo& GetDebugInfo(void) const;
virtual ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const = 0;
+
+ static boost::signals2::signal<void (ScriptFrame& frame, ScriptError *ex, const DebugInfo& di)> OnBreakpoint;
+
+ static void ScriptBreakpoint(ScriptFrame& frame, ScriptError *ex, const DebugInfo& di);
};
I2_CONFIG_API Expression *MakeIndexer(ScopeSpecifier scopeSpec, const String& index);
String m_Package;
};
+class I2_CONFIG_API BreakpointExpression : public DebuggableExpression
+{
+public:
+ BreakpointExpression(const DebugInfo& debugInfo = DebugInfo())
+ : DebuggableExpression(debugInfo)
+ { }
+
+protected:
+ virtual ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
+};
+
}
#endif /* EXPRESSION_H */