diff --git a/src/Attr.cc b/src/Attr.cc index 72b984e924..43542aaade 100644 --- a/src/Attr.cc +++ b/src/Attr.cc @@ -14,7 +14,7 @@ const char* attr_name(attr_tag t) "&read_expire", "&write_expire", "&create_expire", "&raw_output", "&priority", "&group", "&log", "&error_handler", "&type_column", - "(&tracked)", "&deprecated", + "(&tracked)", "&on_change", "&deprecated", }; return attr_names[int(t)]; @@ -405,7 +405,7 @@ void Attributes::CheckAttr(Attr* a) { if ( type->Tag() != TYPE_TABLE ) { - Error("expiration only applicable to tables"); + Error("expiration only applicable to sets/tables"); break; } diff --git a/src/Val.cc b/src/Val.cc index 3d512e6057..f7feb3bd31 100644 --- a/src/Val.cc +++ b/src/Val.cc @@ -1933,7 +1933,7 @@ ListVal* TableVal::RecoverIndex(const HashKey* k) const void TableVal::CallChangeFunc(const Val* index, Val* old_value, OnChangeType tpe) { - if ( ! change_func || ! index ) + if ( ! change_func || ! index || in_change_func ) return; if ( ! table_type->IsSet() && ! old_value ) @@ -1978,12 +1978,15 @@ void TableVal::CallChangeFunc(const Val* index, Val* old_value, OnChangeType tpe if ( ! table_type->IsSet() ) vl.append(old_value->Ref()); + in_change_func = true; f->Call(&vl); Unref(thefunc); } catch ( InterpreterException& e ) { } + + in_change_func = false; } Val* TableVal::Delete(const Val* index) diff --git a/src/Val.h b/src/Val.h index f1ac3c1936..18b9226e42 100644 --- a/src/Val.h +++ b/src/Val.h @@ -865,6 +865,8 @@ protected: PrefixTable* subnets; Val* def_val; Expr* change_func = nullptr; + // prevent recursion of change functions + bool in_change_func = false; }; class RecordVal : public Val, public notifier::Modifiable { diff --git a/testing/btest/Baseline/language.on_change-recurse/output b/testing/btest/Baseline/language.on_change-recurse/output new file mode 100644 index 0000000000..300b6fd502 --- /dev/null +++ b/testing/btest/Baseline/language.on_change-recurse/output @@ -0,0 +1,8 @@ +inserting +change_function, a, 1, 5, TABLE_ELEMENT_NEW +set_change, hi, TABLE_ELEMENT_NEW +changing +change_function, a, 1, 5, TABLE_ELEMENT_CHANGED +deleting +change_function, a, 1, 5, TABLE_ELEMENT_REMOVED +set_change, hi, TABLE_ELEMENT_REMOVED diff --git a/testing/btest/language/on_change-recurse.test b/testing/btest/language/on_change-recurse.test new file mode 100644 index 0000000000..b3cc8d61e0 --- /dev/null +++ b/testing/btest/language/on_change-recurse.test @@ -0,0 +1,30 @@ +# @TEST-EXEC: zeek %INPUT >output +# @TEST-EXEC: btest-diff output + +module TestModule; + +function change_function(t: table[string, int] of count, tpe: TableChange, idxa: string, idxb: int, val: count) + { + print "change_function", idxa, idxb, val, tpe; + t[idxa, idxb] = val; + } + +function set_change(t: set[string], tpe: TableChange, idx: string) + { + print "set_change", idx, tpe; + } + +global t: table[string, int] of count &on_change=change_function; +global s: set[string] &on_change=set_change; + +event zeek_init() + { + print "inserting"; + t["a", 1] = 5; + add s["hi"]; + print "changing"; + t["a", 1] = 2; + print "deleting"; + delete t["a", 1]; + delete s["hi"]; + }