summaryrefslogtreecommitdiffstats
path: root/src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp')
-rw-r--r--src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp359
1 files changed, 359 insertions, 0 deletions
diff --git a/src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp b/src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp
new file mode 100644
index 0000000..c66005f
--- /dev/null
+++ b/src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp
@@ -0,0 +1,359 @@
+/**
+ * @file
+ * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#include "ExpectInvoker.h"
+#include <glog/logging.h>
+
+#ifdef BUILD_AS_PLUGINS
+#include <Pluma/Connector.hpp>
+#endif
+
+#include "uscxml/UUID.h"
+
+#undef USE_TCL_STUBS
+
+namespace uscxml {
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_CONNECTOR
+bool pluginConnect(pluma::Host& host) {
+ host.add( new ExpectInvokerProvider() );
+ return true;
+}
+#endif
+
+Tcl_Interp* ExpectInvoker::_tcl = NULL;
+
+ExpectInvoker::ExpectInvoker() : _eventQueue(NULL) {
+}
+
+ExpectInvoker::~ExpectInvoker() {
+ _eventQueue->stop();
+ exp_cl
+// if (_tcl) {
+// Tcl_DeleteInterp(_tcl);
+// }
+};
+
+boost::shared_ptr<InvokerImpl> ExpectInvoker::create(InterpreterImpl* interpreter) {
+ boost::shared_ptr<ExpectInvoker> invoker = boost::shared_ptr<ExpectInvoker>(new ExpectInvoker());
+ return invoker;
+}
+
+Data ExpectInvoker::getDataModelVariables() {
+ Data data;
+ return data;
+}
+
+void ExpectInvoker::send(const SendRequest& req) {
+ EventContext* ctx = new EventContext();
+ ctx->sendReq = req;
+ ctx->instance = this;
+
+// LOG(ERROR) << "################ " << req;
+
+ std::string eventId = UUID::getUUID();
+ _eventQueue->addEvent(eventId, ExpectInvoker::send, 0, ctx);
+
+// send(ctx, "");
+}
+
+void ExpectInvoker::send(void *userdata, const std::string event) {
+
+ EventContext* ctx = (EventContext*)userdata;
+ if (!ctx)
+ return;
+
+ if (!ctx->instance) {
+ delete(ctx);
+ return;
+ }
+
+ const SendRequest& req = ctx->sendReq;
+
+ if (iequals(req.name, "expect.match")) {
+ int nrCases = req.params.size();
+ struct exp_case *cases = (struct exp_case*)malloc(sizeof(struct exp_case) * (nrCases + 1));
+ memset(cases, 0, sizeof(exp_case) * (nrCases + 1));
+
+ /**
+ exp_end: indicates that no more patterns appear.
+ exp_glob: indicates that the pattern is a glob-style string pattern.
+ exp_exact: indicates that the pattern is an exact string.
+ exp_regexp: indicates that the pattern is a regexp-style string pattern.
+ exp_compiled: indicates that the pattern is a regexp-style string pattern, and that its compiled form is also provided.
+ exp_null: indicates that the pattern is a null (for debugging purposes, a string pattern must also follow).
+ */
+
+ Event::params_t::const_iterator paramIter = req.params.begin();
+ int index = 0;
+ while (paramIter != req.params.end()) {
+ struct exp_case* expCase = &cases[index];
+ size_t colonPos = paramIter->first.find(":");
+ if (colonPos != std::string::npos) {
+ if (paramIter->first.substr(0, colonPos) == "regex") {
+ expCase->type = exp_regexp;
+ } else if(paramIter->first.substr(0, colonPos) == "glob") {
+ expCase->type = exp_glob;
+ } else if(paramIter->first.substr(0, colonPos) == "exact") {
+ expCase->type = exp_exact;
+ } else {
+ // if we can't make sense of the type
+ expCase->type = exp_exact;
+ }
+ } else {
+ expCase->type = exp_regexp;
+ }
+
+ expCase->pattern = strdup(paramIter->second.atom.c_str());
+// LOG(ERROR) << "################ " << expCase->pattern;
+
+ if (expCase->type == exp_regexp) {
+ expCase->re = TclRegComp(expCase->pattern);
+ if (expCase->re == NULL) {
+ LOG(ERROR) << TclGetRegError();
+ expCase->type = exp_null;
+ }
+ }
+ expCase->value = index + 1;
+ paramIter++;
+ index++;
+ }
+
+ assert(index == nrCases);
+
+ cases[nrCases].type = exp_end;
+
+ /**
+ * The functions wait until the output from a process matches one of the
+ * patterns, a specified time period has passed, or an EOF is seen.
+ */
+
+ int rc = 0;
+ // exp_fexpectv won't return on timeout when called in thread
+// rc = exp_fexpectv(ctx->instance->_cmdFP, cases);
+ rc = exp_expectv(ctx->instance->_cmdFD, cases);
+
+ if (rc == EXP_EOF) {
+ Event ev;
+ ev.name = "expect.match.eof";
+ ev.data.compound["buffer"] = Data(exp_buffer, Data::VERBATIM);
+ ctx->instance->returnEvent(ev);
+ } else if (rc == EXP_TIMEOUT) {
+ Event ev;
+ ev.name = "expect.match.timeout";
+ ev.data.compound["buffer"] = Data(exp_buffer, Data::VERBATIM);
+ ctx->instance->returnEvent(ev);
+ } else if (rc == EXP_FULLBUFFER) {
+ Event ev;
+ ev.name = "expect.match.fullbuffer";
+ ev.data.compound["buffer"] = Data(exp_buffer, Data::VERBATIM);
+ ctx->instance->returnEvent(ev);
+ } else if (rc > 0) {
+ rc--; // we started at 1
+ paramIter = req.params.begin();
+ while(rc > 0) {
+ if (paramIter == req.params.end())
+ break;
+ paramIter++;
+ rc--;
+ }
+ if (paramIter != req.params.end()) {
+ Event event;
+
+ size_t colonPos = paramIter->first.find(":");
+ if (colonPos != std::string::npos) {
+ std::string eventName = paramIter->first;
+ event.name = std::string("expect.match.") + eventName.substr(colonPos + 1, eventName.length() - (colonPos + 1));
+ event.data.compound["type"] = Data(paramIter->first.substr(0, colonPos), Data::VERBATIM);
+
+ } else {
+ event.name = std::string("expect.match.") + paramIter->first;
+ event.data.compound["type"] = Data("regex", Data::VERBATIM);
+ }
+
+ event.data.compound["pattern"] = Data(paramIter->second.atom, Data::VERBATIM);
+ event.data.compound["buffer"] = Data(exp_buffer, Data::VERBATIM);
+ event.data.compound["start"] = Data((int)(exp_match - exp_buffer));
+ event.data.compound["end"] = Data((int)(exp_match_end - exp_buffer));
+ event.data.compound["match"] = Data(std::string(exp_buffer).substr(exp_match - exp_buffer, exp_match_end - exp_match), Data::VERBATIM);
+ ctx->instance->returnEvent(event);
+ } else {
+ // exp_fexpectl returned gibberish
+ assert(false);
+ }
+ } else {
+ // exp_fexpectl returned gibberish
+ assert(false);
+ }
+
+ // free our memory
+ for (int i = 0; i < nrCases; i++) {
+ if (cases[i].pattern != NULL)
+ free(cases[i].pattern);
+ if (cases[i].re != NULL)
+ free(cases[i].re);
+ }
+ free(cases);
+
+ } else if (iequals(req.name, "expect.send")) {
+ std::string toSend = unescape(req.content);
+ ctx->instance->_interpreter->getDataModel().replaceExpressions(toSend);
+ fwrite(toSend.c_str(), toSend.length(), 1, ctx->instance->_cmdFP);
+ }
+
+ delete(ctx);
+}
+
+void ExpectInvoker::cancel(const std::string sendId) {
+}
+
+void ExpectInvoker::invoke(const InvokeRequest& req) {
+ if (_eventQueue == NULL) {
+ _eventQueue = new DelayedEventQueue();
+ _eventQueue->start();
+ }
+
+ EventContext* ctx = new EventContext();
+ ctx->invokeReq = req;
+ ctx->instance = this;
+
+ //_eventQueue->addEvent(req.sendid, ExpectInvoker::invoke, 0, ctx);
+ invoke(ctx, "");
+
+}
+
+void ExpectInvoker::invoke(void *userdata, const std::string event) {
+ EventContext* ctx = (EventContext*)userdata;
+
+ if (!ctx)
+ return;
+
+ if (!ctx->instance) {
+ delete(ctx);
+ return;
+ }
+
+ const InvokeRequest& req = ctx->invokeReq;
+
+ // moved here for thread local storage
+ if (ctx->instance->_tcl == NULL) {
+ ctx->instance->_tcl = Tcl_CreateInterp();
+ if (ctx->instance->_tcl) {
+ Tcl_Init(ctx->instance->_tcl);
+ Expect_Init(ctx->instance->_tcl);
+ }
+ ctx->instance->_cmdFP = NULL;
+
+ bool debug = false;
+ Event::getParam(req.params, "debug", debug);
+ if (debug) {
+ exp_is_debugging = 1;
+ } else {
+ exp_is_debugging = 0;
+ }
+
+ int timeout = 20;
+ Event::getParam(req.params, "timeout", timeout);
+ exp_timeout = timeout;
+
+ bool logUser = false;
+ Event::getParam(req.params, "loguser", logUser);
+ if (logUser) {
+ exp_loguser = 1;
+ } else {
+ exp_loguser = 0;
+ }
+
+ // exp_interactive = 1;
+ exp_logfile = 0;
+ // exp_remove_nulls = 1;
+ // exp_ttyinit = 1;
+
+ } else {
+// assert(false);
+ }
+
+ char* cmd = NULL;
+ char** args = NULL;
+ int nrArgs = 0;
+
+ if (req.params.count("spawn")) {
+ // get command
+ std::string command;
+ Event::getParam(req.params, "spawn", command);
+ cmd = strdup(command.c_str());
+
+ // get arguments
+ nrArgs = req.params.count("argument");
+ args = (char**)malloc(sizeof(char*) * nrArgs + 2);
+ args[0] = strdup(command.c_str());
+
+ size_t index = 1;
+ std::pair<Event::params_t::const_iterator, Event::params_t::const_iterator> argIterRange = req.params.equal_range("argument");
+ Event::params_t::const_iterator argIter = argIterRange.first;
+ while(argIter != argIterRange.second) {
+ args[index] = strdup(argIter->second.atom.c_str());
+ argIter++;
+ index++;
+ }
+ args[index] = (char*)0;
+ } else if(req.params.count("command")) {
+
+ }
+
+ // open socket
+ ctx->instance->_cmdFD = exp_spawnv(cmd, args);
+ if (ctx->instance->_cmdFD > 0) {
+ ctx->instance->_cmdFP = fdopen(ctx->instance->_cmdFD, "r+");
+
+ if (ctx->instance->_cmdFP) {
+ // disable buffering
+ setbuf(ctx->instance->_cmdFP,(char *)0);
+ Event event;
+ event.name = "spawn.success";
+ ctx->instance->returnEvent(event);
+ }
+ }
+
+ if (ctx->instance->_cmdFP == NULL || ctx->instance->_cmdFD <= 0) {
+ Event event;
+ event.name = "spawn.failed";
+ event.data.compound["cause"] = Data(strerror(errno), Data::VERBATIM);
+ Tcl_Obj *infoObj = Tcl_GetVar2Ex(_tcl, "errorInfo", NULL, TCL_GLOBAL_ONLY);
+ if (infoObj) {
+ event.data.compound["errorInfo"] = Data(Tcl_GetString(infoObj), Data::VERBATIM);
+ }
+
+ ctx->instance->returnEvent(event);
+ }
+
+ if (cmd)
+ free(cmd);
+
+ if (args) {
+ for (int i = 0; i < nrArgs + 1; i++) {
+ free(args[i]);
+ }
+ free(args);
+ }
+
+}
+
+} \ No newline at end of file