diff --git a/src/Attr.cc b/src/Attr.cc index d63f72a3fd..a4fd4acf71 100644 --- a/src/Attr.cc +++ b/src/Attr.cc @@ -482,6 +482,71 @@ void Attributes::CheckAttr(Attr* a) } break; + case ATTR_ON_CHANGE: + { + if ( type->Tag() != TYPE_TABLE ) + { + Error("&on_change only applicable to tables"); + break; + } + + const Expr *change_func = a->AttrExpr(); + + if ( change_func->Type()->Tag() != TYPE_FUNC || change_func->Type()->AsFuncType()->Flavor() != FUNC_FLAVOR_FUNCTION ) + Error("&on_change attribute is not a function"); + + const FuncType *c_ft = change_func->Type()->AsFuncType(); + + if ( c_ft->YieldType()->Tag() != TYPE_BOOL ) + { + Error("&on_change must yield a value of type bool"); + break; + } + + const TableType *the_table = type->AsTableType(); + + if (the_table->IsUnspecifiedTable()) + break; + + const type_list* args = c_ft->ArgTypes()->Types(); + const type_list* t_indexes = the_table->IndexTypes(); + if ( args->length() != 3 + t_indexes->length() ) + { + Error("&on_change function has incorrect number of arguments"); + break; + } + + if ( ! same_type((*args)[0], the_table->AsTableType()) ) + { + Error("&on_change: first argument must be of same type as table"); + break; + } + + // can't check exact type here yet - the data structures don't exist yet. + if ( (*args)[1]->Tag() != TYPE_ENUM ) + { + Error("&on_change: second argument must be a TableChange enum"); + break; + } + + bool indexmatch = true; + for ( int i = 0; i < t_indexes->length(); i++ ) + { + if ( ! same_type((*args)[2+i], (*t_indexes)[i]) ) + { + Error("&on_change: index types do not match table"); + break; + } + } + + if ( ! same_type((*args)[2+t_indexes->length()], the_table->YieldType()) ) + { + Error("&on_change: value type does not match table"); + break; + } + } + break; + case ATTR_TRACKED: // FIXME: Check here for global ID? break; diff --git a/src/Attr.h b/src/Attr.h index 53d60eda27..e9d75e7ae0 100644 --- a/src/Attr.h +++ b/src/Attr.h @@ -27,6 +27,7 @@ typedef enum { ATTR_ERROR_HANDLER, ATTR_TYPE_COLUMN, // for input framework ATTR_TRACKED, // hidden attribute, tracked by NotifierRegistry + ATTR_ON_CHANGE, // for table change tracking ATTR_DEPRECATED, #define NUM_ATTRS (int(ATTR_DEPRECATED) + 1) } attr_tag; diff --git a/src/Val.cc b/src/Val.cc index 907a29d062..1d8ed9d859 100644 --- a/src/Val.cc +++ b/src/Val.cc @@ -1340,6 +1340,7 @@ TableVal::~TableVal() Unref(def_val); Unref(expire_func); Unref(expire_time); + Unref(change_func); } void TableVal::RemoveAll() @@ -1391,6 +1392,12 @@ void TableVal::SetAttrs(Attributes* a) expire_func = ef->AttrExpr(); expire_func->Ref(); } + auto cf = attrs->FindAttr(ATTR_ON_CHANGE); + if ( cf ) + { + change_func = cf->AttrExpr(); + change_func->Ref(); + } } void TableVal::CheckExpireAttr(attr_tag at) @@ -1926,6 +1933,13 @@ ListVal* TableVal::RecoverIndex(const HashKey* k) const Val* TableVal::Delete(const Val* index) { HashKey* k = ComputeHash(index); + if ( k && change_func ) + { + auto el = AsTable()->Lookup(k); + if ( el ) + { + } + } TableEntryVal* v = k ? AsNonConstTable()->RemoveEntry(k) : 0; Val* va = v ? (v->Value() ? v->Value() : this->Ref()) : 0; diff --git a/src/Val.h b/src/Val.h index 50b3b7a20f..88719171b8 100644 --- a/src/Val.h +++ b/src/Val.h @@ -933,6 +933,7 @@ protected: IterCookie* expire_cookie; PrefixTable* subnets; Val* def_val; + Expr* change_func = nullptr; }; class RecordVal : public Val, public notifier::Modifiable { diff --git a/src/parse.y b/src/parse.y index 6e8b4dc327..7d7deefd81 100644 --- a/src/parse.y +++ b/src/parse.y @@ -5,7 +5,7 @@ // Switching parser table type fixes ambiguity problems. %define lr.type ielr -%expect 105 +%expect 111 %token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY %token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF @@ -24,7 +24,7 @@ %token TOK_ATTR_ADD_FUNC TOK_ATTR_DEFAULT TOK_ATTR_OPTIONAL TOK_ATTR_REDEF %token TOK_ATTR_DEL_FUNC TOK_ATTR_EXPIRE_FUNC %token TOK_ATTR_EXPIRE_CREATE TOK_ATTR_EXPIRE_READ TOK_ATTR_EXPIRE_WRITE -%token TOK_ATTR_RAW_OUTPUT +%token TOK_ATTR_RAW_OUTPUT TOK_ATTR_ON_CHANGE %token TOK_ATTR_PRIORITY TOK_ATTR_LOG TOK_ATTR_ERROR_HANDLER %token TOK_ATTR_TYPE_COLUMN TOK_ATTR_DEPRECATED @@ -1327,6 +1327,8 @@ attr: { $$ = new Attr(ATTR_ADD_FUNC, $3); } | TOK_ATTR_DEL_FUNC '=' expr { $$ = new Attr(ATTR_DEL_FUNC, $3); } + | TOK_ATTR_ON_CHANGE '=' expr + { $$ = new Attr(ATTR_ON_CHANGE, $3); } | TOK_ATTR_EXPIRE_FUNC '=' expr { $$ = new Attr(ATTR_EXPIRE_FUNC, $3); } | TOK_ATTR_EXPIRE_CREATE '=' expr diff --git a/src/scan.l b/src/scan.l index 8a5b1c9fe8..d8792a1470 100644 --- a/src/scan.l +++ b/src/scan.l @@ -298,6 +298,7 @@ when return TOK_WHEN; &read_expire return TOK_ATTR_EXPIRE_READ; &redef return TOK_ATTR_REDEF; &write_expire return TOK_ATTR_EXPIRE_WRITE; +&on_change return TOK_ATTR_ON_CHANGE; @deprecated.* { auto num_files = file_stack.length(); diff --git a/src/types.bif b/src/types.bif index 98d1df0c52..bea4bd0f3d 100644 --- a/src/types.bif +++ b/src/types.bif @@ -225,6 +225,12 @@ type gtp_qos_profile: record; type gtp_private_extension: record; type gtp_gsn_addr: record; +enum TableChange %{ + TABLE_ELEMENT_NEW, + TABLE_ELEMENT_CHANGED, + TABLE_ELEMENT_REMOVED, +%} + module Reporter; enum Level %{