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.