From 034304b9d5c1529c156878cfd28289ffe2db237d Mon Sep 17 00:00:00 2001 From: Johanna Amann Date: Mon, 18 May 2020 14:38:10 -0700 Subject: [PATCH] Make SendEvent callable from all threads This commit refactors the SendEvent call and moves it from the Input ReaderBackend to to MsgThread. This allows all other types of threads to access this functionality. This necessitated a few more changes. Most importantly, one of the ValueToVal methods was moved over to SerialTypes. Whereit arguably belongs - there was nothing that was input-framework specific in that method - and the functionality could come in useful in a number of cases. --- src/input/Manager.cc | 278 +----------------- src/input/Manager.h | 13 +- src/input/ReaderBackend.cc | 29 -- src/input/ReaderBackend.h | 12 - src/threading/BasicThread.h | 2 +- src/threading/Manager.cc | 55 ++++ src/threading/Manager.h | 12 + src/threading/MsgThread.cc | 30 ++ src/threading/MsgThread.h | 14 +- src/threading/SerialTypes.cc | 217 +++++++++++++- src/threading/SerialTypes.h | 20 ++ .../errout | 2 +- 12 files changed, 353 insertions(+), 331 deletions(-) diff --git a/src/input/Manager.cc b/src/input/Manager.cc index dcbc4f53c0..5a1edb3200 100644 --- a/src/input/Manager.cc +++ b/src/input/Manager.cc @@ -26,14 +26,6 @@ using namespace std; using threading::Value; using threading::Field; -static void delete_value_ptr_array(Value** vals, int num_fields) - { - for ( int i = 0; i < num_fields; ++i ) - delete vals[i]; - - delete [] vals; - } - /** * InputHashes are used as Dictionaries to store the value and index hashes * for all lines currently stored in a table. Index hash is stored as @@ -1087,7 +1079,7 @@ void Manager::SendEntry(ReaderFrontend* reader, Value* *vals) else assert(false); - delete_value_ptr_array(vals, readFields); + Value::delete_value_ptr_array(vals, readFields); } int Manager::SendEntryTable(Stream* i, const Value* const *vals) @@ -1469,7 +1461,7 @@ void Manager::Put(ReaderFrontend* reader, Value* *vals) else assert(false); - delete_value_ptr_array(vals, readFields); + Value::delete_value_ptr_array(vals, readFields); } int Manager::SendEventStreamEvent(Stream* i, EnumVal* type, const Value* const *vals) @@ -1774,7 +1766,7 @@ bool Manager::Delete(ReaderFrontend* reader, Value* *vals) return false; } - delete_value_ptr_array(vals, readVals); + Value::delete_value_ptr_array(vals, readVals); return success; } @@ -1799,68 +1791,6 @@ bool Manager::CallPred(Func* pred_func, const int numvals, ...) const return result; } -// Raise everything in here as warnings so it is passed to scriptland without -// looking "fatal". In addition to these warnings, ReaderBackend will queue -// one reporter message. -bool Manager::SendEvent(ReaderFrontend* reader, const string& name, const int num_vals, Value* *vals) const - { - Stream *i = FindStream(reader); - if ( i == nullptr ) - { - reporter->InternalWarning("Unknown reader %s in SendEvent for event %s", reader->Name(), name.c_str()); - delete_value_ptr_array(vals, num_vals); - return false; - } - - EventHandler* handler = event_registry->Lookup(name); - if ( handler == nullptr ) - { - Warning(i, "Event %s not found", name.c_str()); - delete_value_ptr_array(vals, num_vals); - return false; - } - -#ifdef DEBUG - DBG_LOG(DBG_INPUT, "SendEvent for event %s with %d vals", - name.c_str(), num_vals); -#endif - - RecordType *type = handler->FType()->Args(); - int num_event_vals = type->NumFields(); - if ( num_vals != num_event_vals ) - { - Warning(i, "Wrong number of values for event %s", name.c_str()); - delete_value_ptr_array(vals, num_vals); - return false; - } - - bool convert_error = false; - - zeek::Args vl; - vl.reserve(num_vals); - - for ( int j = 0; j < num_vals; j++) - { - Val* v = ValueToVal(i, vals[j], convert_error); - vl.emplace_back(AdoptRef{}, v); - - if ( v && ! convert_error && ! same_type(type->FieldType(j), v->Type()) ) - { - convert_error = true; - type->FieldType(j)->Error("SendEvent types do not match", v->Type()); - } - } - - delete_value_ptr_array(vals, num_vals); - - if ( convert_error ) - return false; - else if ( handler ) - mgr.Enqueue(handler, std::move(vl), SOURCE_LOCAL); - - return true; -} - void Manager::SendEvent(EventHandlerPtr ev, const int numvals, ...) const { zeek::Args vl; @@ -2240,9 +2170,9 @@ HashKey* Manager::HashValues(const int num_elements, const Value* const *vals) c } // convert threading value to Bro value -// have_error is a reference to a boolean which is set to true as soon as an error occured. +// have_error is a reference to a boolean which is set to true as soon as an error occurs. // When have_error is set to true at the beginning of the function, it is assumed that -// an error already occured in the past and processing is aborted. +// an error already occurred in the past and processing is aborted. Val* Manager::ValueToVal(const Stream* i, const Value* val, BroType* request_type, bool& have_error) const { if ( have_error ) @@ -2397,204 +2327,6 @@ Val* Manager::ValueToVal(const Stream* i, const Value* val, BroType* request_typ return nullptr; } -Val* Manager::ValueToVal(const Stream* i, const Value* val, bool& have_error) const - { - if ( have_error ) - return nullptr; - - if ( ! val->present ) - return nullptr; // unset field - - switch ( val->type ) { - case TYPE_BOOL: - return val_mgr->Bool(val->val.int_val)->Ref(); - - case TYPE_INT: - return val_mgr->Int(val->val.int_val).release(); - - case TYPE_COUNT: - case TYPE_COUNTER: - return val_mgr->Count(val->val.int_val).release(); - - case TYPE_DOUBLE: - case TYPE_TIME: - case TYPE_INTERVAL: - return new Val(val->val.double_val, val->type); - - case TYPE_STRING: - { - BroString *s = new BroString((const u_char*)val->val.string_val.data, val->val.string_val.length, true); - return new StringVal(s); - } - - case TYPE_PORT: - return val_mgr->Port(val->val.port_val.port, val->val.port_val.proto)->Ref(); - - case TYPE_ADDR: - { - IPAddr* addr = nullptr; - switch ( val->val.addr_val.family ) { - case IPv4: - addr = new IPAddr(val->val.addr_val.in.in4); - break; - - case IPv6: - addr = new IPAddr(val->val.addr_val.in.in6); - break; - - default: - assert(false); - } - - AddrVal* addrval = new AddrVal(*addr); - delete addr; - return addrval; - } - - case TYPE_SUBNET: - { - IPAddr* addr = nullptr; - switch ( val->val.subnet_val.prefix.family ) { - case IPv4: - addr = new IPAddr(val->val.subnet_val.prefix.in.in4); - break; - - case IPv6: - addr = new IPAddr(val->val.subnet_val.prefix.in.in6); - break; - - default: - assert(false); - } - - SubNetVal* subnetval = new SubNetVal(*addr, val->val.subnet_val.length); - delete addr; - return subnetval; - } - - case TYPE_PATTERN: - { - RE_Matcher* re = new RE_Matcher(val->val.pattern_text_val); - re->Compile(); - return new PatternVal(re); - } - - case TYPE_TABLE: - { - IntrusivePtr set_index; - if ( val->val.set_val.size == 0 && val->subtype == TYPE_VOID ) - // don't know type - unspecified table. - set_index = make_intrusive(); - else - { - // all entries have to have the same type... - TypeTag stag = val->subtype; - if ( stag == TYPE_VOID ) - TypeTag stag = val->val.set_val.vals[0]->type; - - IntrusivePtr index_type; - - if ( stag == TYPE_ENUM ) - { - // Enums are not a base-type, so need to look it up. - const auto& sv = val->val.set_val.vals[0]->val.string_val; - std::string enum_name(sv.data, sv.length); - auto enum_id = global_scope()->Lookup(enum_name); - - if ( ! enum_id ) - { - Warning(i, "Value '%s' for stream '%s' is not a valid enum.", - enum_name.data(), i->name.c_str()); - - have_error = true; - return nullptr; - } - - index_type = {NewRef{}, enum_id->Type()->AsEnumType()}; - } - else - index_type = base_type(stag); - - set_index = make_intrusive(index_type); - set_index->Append(std::move(index_type)); - } - - auto s = make_intrusive(std::move(set_index), nullptr); - TableVal* t = new TableVal(std::move(s)); - for ( int j = 0; j < val->val.set_val.size; j++ ) - { - Val* assignval = ValueToVal(i, val->val.set_val.vals[j], have_error); - - t->Assign(assignval, nullptr); - Unref(assignval); // index is not consumed by assign. - } - - return t; - } - - case TYPE_VECTOR: - { - IntrusivePtr type; - - if ( val->val.vector_val.size == 0 && val->subtype == TYPE_VOID ) - // don't know type - unspecified table. - type = base_type(TYPE_ANY); - else - { - // all entries have to have the same type... - if ( val->subtype == TYPE_VOID ) - type = base_type(val->val.vector_val.vals[0]->type); - else - type = base_type(val->subtype); - } - - auto vt = make_intrusive(std::move(type)); - VectorVal* v = new VectorVal(vt.get()); - - for ( int j = 0; j < val->val.vector_val.size; j++ ) - v->Assign(j, ValueToVal(i, val->val.vector_val.vals[j], have_error)); - - return v; - } - - case TYPE_ENUM: { - // Convert to string first to not have to deal with missing - // \0's... - string enum_string(val->val.string_val.data, val->val.string_val.length); - - // let's try looking it up by global ID. - auto id = lookup_ID(enum_string.c_str(), GLOBAL_MODULE_NAME); - if ( ! id || ! id->IsEnumConst() ) - { - Warning(i, "Value '%s' for stream '%s' is not a valid enum.", - enum_string.c_str(), i->name.c_str()); - - have_error = true; - return nullptr; - } - - EnumType* t = id->Type()->AsEnumType(); - int intval = t->Lookup(id->ModuleName(), id->Name()); - if ( intval < 0 ) - { - Warning(i, "Enum value '%s' for stream '%s' not found.", - enum_string.c_str(), i->name.c_str()); - - have_error = true; - return nullptr; - } - - return t->GetVal(intval).release(); - } - - default: - reporter->InternalError("Unsupported type for input_read in stream %s", i->name.c_str()); - } - - assert(false); - return nullptr; - } - Manager::Stream* Manager::FindStream(const string &name) const { for ( auto s = readers.begin(); s != readers.end(); ++s ) diff --git a/src/input/Manager.h b/src/input/Manager.h index 191692f9e6..c24b930d64 100644 --- a/src/input/Manager.h +++ b/src/input/Manager.h @@ -118,7 +118,6 @@ protected: friend class PutMessage; friend class DeleteMessage; friend class ClearMessage; - friend class SendEventMessage; friend class SendEntryMessage; friend class EndCurrentSendMessage; friend class ReaderClosedMessage; @@ -142,11 +141,6 @@ protected: void SendEntry(ReaderFrontend* reader, threading::Value* *vals); void EndCurrentSend(ReaderFrontend* reader); - // Allows readers to directly send Bro events. The num_vals and vals - // must be the same the named event expects. Takes ownership of - // threading::Value fields. - bool SendEvent(ReaderFrontend* reader, const std::string& name, const int num_vals, threading::Value* *vals) const; - // Instantiates a new ReaderBackend of the given type (note that // doing so creates a new thread!). ReaderBackend* CreateBackend(ReaderFrontend* frontend, EnumVal* tag); @@ -227,14 +221,9 @@ private: // startpos. int CopyValue(char *data, const int startpos, const threading::Value* val) const; - // Convert Threading::Value to an internal Bro Type (works also with - // Records). + // Convert Threading::Value to an internal Bro Type (works with Records). Val* ValueToVal(const Stream* i, const threading::Value* val, BroType* request_type, bool& have_error) const; - // Convert Threading::Value to an internal Bro type just using the information given in the threading::Value. - // This allows more flexibility, especially given structures in script-land that contain any types. - Val* ValueToVal(const Stream* i, const threading::Value* val, bool& have_error) const; - // Convert Threading::Value to an internal Bro list type. Val* ValueToIndexVal(const Stream* i, int num_fields, const RecordType* type, const threading::Value* const *vals, bool& have_error) const; diff --git a/src/input/ReaderBackend.cc b/src/input/ReaderBackend.cc index 90050a6438..7e0fbd7685 100644 --- a/src/input/ReaderBackend.cc +++ b/src/input/ReaderBackend.cc @@ -54,30 +54,6 @@ public: private: }; -class SendEventMessage final : public threading::OutputMessage { -public: - SendEventMessage(ReaderFrontend* reader, const char* name, const int num_vals, Value* *val) - : threading::OutputMessage("SendEvent", reader), - name(copy_string(name)), num_vals(num_vals), val(val) {} - - ~SendEventMessage() override { delete [] name; } - - bool Process() override - { - bool success = input_mgr->SendEvent(Object(), name, num_vals, val); - - if ( ! success ) - reporter->Error("SendEvent for event %s failed", name); - - return true; // We do not want to die if sendEvent fails because the event did not return. - } - -private: - const char* name; - const int num_vals; - Value* *val; -}; - class ReaderErrorMessage final : public threading::OutputMessage { public: @@ -229,11 +205,6 @@ void ReaderBackend::Clear() SendOut(new ClearMessage(frontend)); } -void ReaderBackend::SendEvent(const char* name, const int num_vals, Value* *vals) - { - SendOut(new SendEventMessage(frontend, name, num_vals, vals)); - } - void ReaderBackend::EndCurrentSend() { SendOut(new EndCurrentSendMessage(frontend)); diff --git a/src/input/ReaderBackend.h b/src/input/ReaderBackend.h index d158d0972c..2748d1e2d2 100644 --- a/src/input/ReaderBackend.h +++ b/src/input/ReaderBackend.h @@ -280,18 +280,6 @@ protected: */ virtual bool DoHeartbeat(double network_time, double current_time) = 0; - /** - * Method allowing a reader to send a specified Bro event. Vals must - * match the values expected by the bro event. - * - * @param name name of the bro event to send - * - * @param num_vals number of entries in \a vals - * - * @param vals the values to be given to the event - */ - void SendEvent(const char* name, const int num_vals, threading::Value* *vals); - // Content-sending-functions (simple mode). Include table-specific // functionality that simply is not used if we have no table. diff --git a/src/threading/BasicThread.h b/src/threading/BasicThread.h index fa1e097536..fa65e070ee 100644 --- a/src/threading/BasicThread.h +++ b/src/threading/BasicThread.h @@ -37,7 +37,7 @@ public: /** * Returns a descriptive name for the thread. If not set via - * SetName(). If not set, a default name is choosen automatically. + * SetName(), a default name is chosen automatically. * * This method is safe to call from any thread. */ diff --git a/src/threading/Manager.cc b/src/threading/Manager.cc index e5562a0ddd..666edff142 100644 --- a/src/threading/Manager.cc +++ b/src/threading/Manager.cc @@ -5,6 +5,7 @@ #include "NetVar.h" #include "iosource/Manager.h" +#include "Event.h" using namespace threading; @@ -128,6 +129,60 @@ void Manager::StartHeartbeatTimer() timer_mgr->Add(new HeartbeatTimer(network_time + BifConst::Threading::heartbeat_interval)); } +// Raise everything in here as warnings so it is passed to scriptland without +// looking "fatal". In addition to these warnings, ReaderBackend will queue +// one reporter message. +bool Manager::SendEvent(MsgThread* thread, const std::string& name, const int num_vals, Value* *vals) const + { + EventHandler* handler = event_registry->Lookup(name); + if ( handler == nullptr ) + { + reporter->Warning("Thread %s: Event %s not found", thread->Name(), name.c_str()); + Value::delete_value_ptr_array(vals, num_vals); + return false; + } + +#ifdef DEBUG + DBG_LOG(DBG_INPUT, "Thread %s: SendEvent for event %s with %d vals", + thread->Name(), name.c_str(), num_vals); +#endif + + RecordType *type = handler->FType()->Args(); + int num_event_vals = type->NumFields(); + if ( num_vals != num_event_vals ) + { + reporter->Warning("Thread %s: Wrong number of values for event %s", thread->Name(), name.c_str()); + Value::delete_value_ptr_array(vals, num_vals); + return false; + } + + bool convert_error = false; + + zeek::Args vl; + vl.reserve(num_vals); + + for ( int j = 0; j < num_vals; j++) + { + Val* v = Value::ValueToVal(std::string("thread ") + thread->Name(), vals[j], convert_error); + vl.emplace_back(AdoptRef{}, v); + + if ( v && ! convert_error && ! same_type(type->FieldType(j), v->Type()) ) + { + convert_error = true; + type->FieldType(j)->Error("SendEvent types do not match", v->Type()); + } + } + + Value::delete_value_ptr_array(vals, num_vals); + + if ( convert_error ) + return false; + else if ( handler ) + mgr.Enqueue(handler, std::move(vl), SOURCE_LOCAL); + + return true; + } + void Manager::Flush() { bool do_beat = false; diff --git a/src/threading/Manager.h b/src/threading/Manager.h index 2c5b05d9b5..6429baf8a4 100644 --- a/src/threading/Manager.h +++ b/src/threading/Manager.h @@ -88,6 +88,18 @@ public: */ void KillThreads(); + /** + * Allows threads to directly send Zeek events. The num_vals and vals must be + * the same the named event expects. Takes ownership of threading::Value fields. + * + * @param thread Thread raising the event + * @param name Name of event being raised + * @param num_vals Number of values passed to the event + * @param vals Values passed to the event + * @returns True on success false on failure. + */ + bool SendEvent(MsgThread* thread, const std::string& name, const int num_vals, threading::Value* *vals) const; + protected: friend class BasicThread; friend class MsgThread; diff --git a/src/threading/MsgThread.cc b/src/threading/MsgThread.cc index 3d2fa66271..f1fd2897d2 100644 --- a/src/threading/MsgThread.cc +++ b/src/threading/MsgThread.cc @@ -126,6 +126,31 @@ private: } +// An event that the child wants to pass into the main event queue +class SendEventMessage final : public OutputMessage { +public: + SendEventMessage(MsgThread* thread, const char* name, const int num_vals, Value* *val) + : OutputMessage("SendEvent", thread), + name(copy_string(name)), num_vals(num_vals), val(val) {} + + ~SendEventMessage() override { delete [] name; } + + bool Process() override + { + bool success = thread_mgr->SendEvent(Object(), name, num_vals, val); + + if ( ! success ) + reporter->Error("SendEvent for event %s failed", name); + + return true; // We do not want to die if sendEvent fails because the event did not return. + } + +private: + const char* name; + const int num_vals; + Value* *val; +}; + ////// Methods. Message::~Message() @@ -363,6 +388,11 @@ void MsgThread::SendOut(BasicOutputMessage* msg, bool force) flare.Fire(); } +void MsgThread::SendEvent(const char* name, const int num_vals, Value* *vals) + { + SendOut(new SendEventMessage(this, name, num_vals, vals)); + } + BasicOutputMessage* MsgThread::RetrieveOut() { BasicOutputMessage* msg = queue_out.Get(); diff --git a/src/threading/MsgThread.h b/src/threading/MsgThread.h index e5c0e3a910..8bda2d40b8 100644 --- a/src/threading/MsgThread.h +++ b/src/threading/MsgThread.h @@ -60,6 +60,18 @@ public: */ void SendOut(BasicOutputMessage* msg) { return SendOut(msg, false); } + /** + * Allows the child thread to send a specified Zeek event. The given Vals + * must match the values expected by the event. + * + * @param name name of the bro event to send + * + * @param num_vals number of entries in \a vals + * + * @param vals the values to be given to the event + */ + void SendEvent(const char* name, const int num_vals, threading::Value* *vals); + /** * Reports an informational message from the child thread. The main * thread will pass this to the Reporter once received. @@ -393,7 +405,7 @@ protected: }; /** - * A paremeterized InputMessage that stores a pointer to an argument object. + * A parameterized InputMessage that stores a pointer to an argument object. * Normally, the objects will be used from the Process() callback. */ template diff --git a/src/threading/SerialTypes.cc b/src/threading/SerialTypes.cc index a115c37351..7b686fc30d 100644 --- a/src/threading/SerialTypes.cc +++ b/src/threading/SerialTypes.cc @@ -4,6 +4,14 @@ #include "SerialTypes.h" #include "SerializationFormat.h" #include "Reporter.h" +// The following are required for ValueToVal. +#include "Val.h" +#include "BroString.h" +#include "RE.h" +#include "module_util.h" +#include "ID.h" +#include "Expr.h" +#include "Scope.h" using namespace threading; @@ -346,7 +354,6 @@ bool Value::Write(SerializationFormat* fmt) const case IPv6: return fmt->Write((char)6, "addr-family") && fmt->Write(val.addr_val.in.in6, "addr-in6"); - break; } // Can't be reached. @@ -366,7 +373,6 @@ bool Value::Write(SerializationFormat* fmt) const case IPv6: return fmt->Write((char)6, "subnet-family") && fmt->Write(val.subnet_val.prefix.in.in6, "subnet-in6"); - break; } // Can't be reached. @@ -417,5 +423,212 @@ bool Value::Write(SerializationFormat* fmt) const type_name(type)); } + // unreachable return false; } + +void Value::delete_value_ptr_array(Value** vals, int num_fields) + { + for ( int i = 0; i < num_fields; ++i ) + delete vals[i]; + + delete [] vals; + } + +Val* Value::ValueToVal(const std::string& source, const Value* val, bool& have_error) + { + if ( have_error ) + return nullptr; + + if ( ! val->present ) + return nullptr; // unset field + + switch ( val->type ) { + case TYPE_BOOL: + return val_mgr->Bool(val->val.int_val)->Ref(); + + case TYPE_INT: + return val_mgr->Int(val->val.int_val).release(); + + case TYPE_COUNT: + case TYPE_COUNTER: + return val_mgr->Count(val->val.int_val).release(); + + case TYPE_DOUBLE: + case TYPE_TIME: + case TYPE_INTERVAL: + return new Val(val->val.double_val, val->type); + + case TYPE_STRING: + { + BroString *s = new BroString((const u_char*)val->val.string_val.data, val->val.string_val.length, true); + return new StringVal(s); + } + + case TYPE_PORT: + return val_mgr->Port(val->val.port_val.port, val->val.port_val.proto)->Ref(); + + case TYPE_ADDR: + { + IPAddr* addr = nullptr; + switch ( val->val.addr_val.family ) { + case IPv4: + addr = new IPAddr(val->val.addr_val.in.in4); + break; + + case IPv6: + addr = new IPAddr(val->val.addr_val.in.in6); + break; + + default: + assert(false); + } + + AddrVal* addrval = new AddrVal(*addr); + delete addr; + return addrval; + } + + case TYPE_SUBNET: + { + IPAddr* addr = nullptr; + switch ( val->val.subnet_val.prefix.family ) { + case IPv4: + addr = new IPAddr(val->val.subnet_val.prefix.in.in4); + break; + + case IPv6: + addr = new IPAddr(val->val.subnet_val.prefix.in.in6); + break; + + default: + assert(false); + } + + SubNetVal* subnetval = new SubNetVal(*addr, val->val.subnet_val.length); + delete addr; + return subnetval; + } + + case TYPE_PATTERN: + { + RE_Matcher* re = new RE_Matcher(val->val.pattern_text_val); + re->Compile(); + return new PatternVal(re); + } + + case TYPE_TABLE: + { + IntrusivePtr set_index; + if ( val->val.set_val.size == 0 && val->subtype == TYPE_VOID ) + // don't know type - unspecified table. + set_index = make_intrusive(); + else + { + // all entries have to have the same type... + TypeTag stag = val->subtype; + if ( stag == TYPE_VOID ) + TypeTag stag = val->val.set_val.vals[0]->type; + + IntrusivePtr index_type; + + if ( stag == TYPE_ENUM ) + { + // Enums are not a base-type, so need to look it up. + const auto& sv = val->val.set_val.vals[0]->val.string_val; + std::string enum_name(sv.data, sv.length); + auto enum_id = global_scope()->Lookup(enum_name); + + if ( ! enum_id ) + { + reporter->Warning("Value '%s' of source '%s' is not a valid enum.", + enum_name.data(), source.c_str()); + + have_error = true; + return nullptr; + } + + index_type = {NewRef{}, enum_id->Type()->AsEnumType()}; + } + else + index_type = base_type(stag); + + set_index = make_intrusive(index_type); + set_index->Append(std::move(index_type)); + } + + auto s = make_intrusive(std::move(set_index), nullptr); + TableVal* t = new TableVal(std::move(s)); + for ( int j = 0; j < val->val.set_val.size; j++ ) + { + Val* assignval = ValueToVal(source, val->val.set_val.vals[j], have_error); + + t->Assign(assignval, nullptr); + Unref(assignval); // index is not consumed by assign. + } + + return t; + } + + case TYPE_VECTOR: + { + IntrusivePtr type; + + if ( val->val.vector_val.size == 0 && val->subtype == TYPE_VOID ) + // don't know type - unspecified table. + type = base_type(TYPE_ANY); + else + { + // all entries have to have the same type... + if ( val->subtype == TYPE_VOID ) + type = base_type(val->val.vector_val.vals[0]->type); + else + type = base_type(val->subtype); + } + + auto vt = make_intrusive(std::move(type)); + VectorVal* v = new VectorVal(vt.get()); + + for ( int j = 0; j < val->val.vector_val.size; j++ ) + v->Assign(j, ValueToVal(source, val->val.vector_val.vals[j], have_error)); + + return v; + } + + case TYPE_ENUM: { + // Convert to string first to not have to deal with missing + // \0's... + std::string enum_string(val->val.string_val.data, val->val.string_val.length); + + // let's try looking it up by global ID. + auto id = lookup_ID(enum_string.c_str(), GLOBAL_MODULE_NAME); + if ( ! id || ! id->IsEnumConst() ) + { + reporter->Warning("Value '%s' for source '%s' is not a valid enum.", + enum_string.c_str(), source.c_str()); + + have_error = true; + return nullptr; + } + + EnumType* t = id->Type()->AsEnumType(); + int intval = t->Lookup(id->ModuleName(), id->Name()); + if ( intval < 0 ) + { + reporter->Warning("Enum value '%s' for source '%s' not found.", + enum_string.c_str(), source.c_str()); + + have_error = true; + return nullptr; + } + + return t->GetVal(intval).release(); + } + + default: + reporter->InternalError("Unsupported type in SerialTypes::ValueToVal from source %s", source.c_str()); + } + + assert(false); + return nullptr; + } diff --git a/src/threading/SerialTypes.h b/src/threading/SerialTypes.h index f424eb9143..0969e5b6db 100644 --- a/src/threading/SerialTypes.h +++ b/src/threading/SerialTypes.h @@ -187,6 +187,26 @@ struct Value { * method is thread-safe. */ static bool IsCompatibleType(BroType* t, bool atomic_only=false); + /** + * Convenience function to delete an array of value pointers. + * @param vals Array of values + * @param num_fields Number of members + */ + static void delete_value_ptr_array(Value** vals, int num_fields); + + /** + * Convert threading::Value to an internal Zeek type, just using the information given in the threading::Value. + * + * @param source Name of the source of this threading value. This is used for warnings that are raised + * in case an error occurs. + * @param val Threading Value to convert to a Zeek Val. + * @param have_error Reference to a boolean. This should be set to false when passed in and is set to true + * in case an error occurs. If this is set to false when the function is called, the function + * immediately aborts. + * @return Val representation of the threading::Value. nullptr on error. + */ + static Val* ValueToVal(const std::string& source, const threading::Value* val, bool& have_error); + private: friend class ::IPAddr; Value(const Value& other) { } // Disabled. diff --git a/testing/btest/Baseline/scripts.base.frameworks.input.config.errors/errout b/testing/btest/Baseline/scripts.base.frameworks.input.config.errors/errout index 792f50d8c8..2b835abe07 100644 --- a/testing/btest/Baseline/scripts.base.frameworks.input.config.errors/errout +++ b/testing/btest/Baseline/scripts.base.frameworks.input.config.errors/errout @@ -5,7 +5,7 @@ warning: ../configfile/Input::READER_CONFIG: Field: testbool Invalid value for b warning: ../configfile/Input::READER_CONFIG: Could not convert line 'testbool A B' to value. Ignoring line. warning: ../configfile/Input::READER_CONFIG: String 'A' contained no parseable number warning: ../configfile/Input::READER_CONFIG: Could not convert line 'testcount A' to value. Ignoring line. -warning: Value 'unknown' for stream 'configuration' is not a valid enum. +warning: Value 'unknown' for source 'thread ../configfile/Input::READER_CONFIG' is not a valid enum. error: SendEvent for event InputConfig::new_value failed warning: ../configfile/Input::READER_CONFIG: Option 'testbooool' does not exist. Ignoring line. warning: ../configfile/Input::READER_CONFIG: Option 'test_any' has type 'any', which is not supported for file input. Ignoring line.