rule.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. /*
  2. * ECAL
  3. *
  4. * Copyright 2020 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the MIT
  7. * License, If a copy of the MIT License was not distributed with this
  8. * file, You can obtain one at https://opensource.org/licenses/MIT.
  9. */
  10. package engine
  11. import (
  12. "bytes"
  13. "encoding/json"
  14. "fmt"
  15. "regexp"
  16. "sort"
  17. "strings"
  18. "sync"
  19. "devt.de/krotik/common/errorutil"
  20. "devt.de/krotik/common/sortutil"
  21. )
  22. /*
  23. Rule models a matching rule for event receivers (actions). A rule has 3 possible
  24. matching criteria:
  25. - Match on event kinds: A list of strings in dot notation which describes event kinds. May
  26. contain '*' characters as wildcards (e.g. core.tests.*).
  27. - Match on event cascade scope: A list of strings in dot notation which describe the
  28. required scopes of an event cascade.
  29. - Match on event state: A simple list of required key / value states in the event
  30. state. Nil values can be used as wildcards (i.e. match is only on key).
  31. Rules have priorities (0 being the highest) and may suppress each other.
  32. */
  33. type Rule struct {
  34. Name string // Name of the rule
  35. Desc string // Description of the rule (optional)
  36. KindMatch []string // Match on event kinds
  37. ScopeMatch []string // Match on event cascade scope
  38. StateMatch map[string]interface{} // Match on event state
  39. Priority int // Priority of the rule
  40. SuppressionList []string // List of suppressed rules by this rule
  41. Action RuleAction // Action of the rule
  42. }
  43. /*
  44. CopyAs returns a shallow copy of this rule with a new name.
  45. */
  46. func (r *Rule) CopyAs(newName string) *Rule {
  47. return &Rule{
  48. Name: newName,
  49. Desc: r.Desc,
  50. KindMatch: r.KindMatch,
  51. ScopeMatch: r.ScopeMatch,
  52. StateMatch: r.StateMatch,
  53. Priority: r.Priority,
  54. SuppressionList: r.SuppressionList,
  55. Action: r.Action,
  56. }
  57. }
  58. func (r *Rule) String() string {
  59. sm, _ := json.Marshal(r.StateMatch)
  60. return fmt.Sprintf("Rule:%s [%s] (Priority:%v Kind:%v Scope:%v StateMatch:%s Suppress:%v)",
  61. r.Name, strings.TrimSpace(r.Desc), r.Priority, r.KindMatch, r.ScopeMatch, sm, r.SuppressionList)
  62. }
  63. /*
  64. RuleAction is an action which is executed by a matching rule.
  65. */
  66. type RuleAction func(p Processor, m Monitor, e *Event) error
  67. /*
  68. RuleIndex is an index for rules. It takes the form of a tree structure in which
  69. incoming events are matched level by level (e.g. event of kind core.task1.step1
  70. is first matched by kind "core" then "task1" and then "step1". At the leaf of
  71. the index tree it may then be matched on a state condition).
  72. */
  73. type RuleIndex interface {
  74. /*
  75. AddRule adds a new rule to the index.
  76. */
  77. AddRule(rule *Rule) error
  78. /*
  79. IsTriggering checks if a given event triggers a rule in this index.
  80. */
  81. IsTriggering(event *Event) bool
  82. /*
  83. Match returns all rules in this index which match a given event. This
  84. method does a full matching check including state matching.
  85. */
  86. Match(event *Event) []*Rule
  87. /*
  88. String returns a string representation of this rule index and all subindexes.
  89. */
  90. String() string
  91. /*
  92. Rules returns all rules with the given prefix in the name. Use the empty
  93. string to return all rules.
  94. */
  95. Rules() map[string]*Rule
  96. }
  97. /*
  98. ruleSubIndex is a sub index used by a rule index.
  99. */
  100. type ruleSubIndex interface {
  101. /*
  102. type returns the type of the rule sub index.
  103. */
  104. Type() string
  105. /*
  106. addRuleAtLevel adds a new rule to the index at a specific level. The
  107. level is described by a part of the rule kind match.
  108. */
  109. addRuleAtLevel(rule *Rule, kindMatchLevel []string)
  110. /*
  111. isTriggeringAtLevel checks if a given event triggers a rule at the given
  112. level of the index.
  113. */
  114. isTriggeringAtLevel(event *Event, level int) bool
  115. /*
  116. matchAtLevel returns all rules in this index which match a given event
  117. at the given level. This method does a full matching check including
  118. state matching.
  119. */
  120. matchAtLevel(event *Event, level int) []*Rule
  121. /*
  122. stringIndent returns a string representation with a given indentation of this
  123. rule index and all subindexes.
  124. */
  125. stringIndent(indent string) string
  126. }
  127. /*
  128. ruleIndexRoot models the index root node.
  129. */
  130. type ruleIndexRoot struct {
  131. *RuleIndexKind
  132. rules map[string]*Rule
  133. }
  134. /*
  135. AddRule adds a new rule to the index.
  136. */
  137. func (r *ruleIndexRoot) AddRule(rule *Rule) error {
  138. if _, ok := r.rules[rule.Name]; ok {
  139. return fmt.Errorf("Cannot add rule %v twice", rule.Name)
  140. }
  141. r.rules[rule.Name] = rule
  142. return r.RuleIndexKind.AddRule(rule)
  143. }
  144. /*
  145. Rules returns all rules with the given prefix in the name. Use the empty
  146. string to return all rules.
  147. */
  148. func (r *ruleIndexRoot) Rules() map[string]*Rule {
  149. return r.rules
  150. }
  151. /*
  152. NewRuleIndex creates a new rule container for efficient event matching.
  153. */
  154. func NewRuleIndex() RuleIndex {
  155. return &ruleIndexRoot{newRuleIndexKind(), make(map[string]*Rule)}
  156. }
  157. /*
  158. Rule index types
  159. */
  160. const (
  161. typeRuleIndexKind = "RuleIndexKind"
  162. typeRuleIndexState = "RuleIndexState"
  163. typeRuleIndexAll = "RuleIndexAll"
  164. )
  165. // Rule Index Kind
  166. // ===============
  167. /*
  168. RuleIndexKind data structure.
  169. */
  170. type RuleIndexKind struct {
  171. id uint64 // Id of this rule index
  172. kindAllMatch []ruleSubIndex // Rules with target all events of a specific category
  173. kindSingleMatch map[string][]ruleSubIndex // Rules which target specific event kinds
  174. count int // Number of loaded rules
  175. }
  176. /*
  177. newRuleIndexKind creates a new rule index matching on event kind.
  178. */
  179. func newRuleIndexKind() *RuleIndexKind {
  180. return &RuleIndexKind{
  181. newRuleIndexID(),
  182. make([]ruleSubIndex, 0),
  183. make(map[string][]ruleSubIndex),
  184. 0,
  185. }
  186. }
  187. /*
  188. Type returns the type of the rule sub index.
  189. */
  190. func (ri *RuleIndexKind) Type() string {
  191. return typeRuleIndexKind
  192. }
  193. /*
  194. AddRule adds a new rule to the index.
  195. */
  196. func (ri *RuleIndexKind) AddRule(rule *Rule) error {
  197. // Check essential rule attributes
  198. if rule.KindMatch == nil || len(rule.KindMatch) == 0 {
  199. return fmt.Errorf("Cannot add rule without a kind match: %v", rule.Name)
  200. } else if rule.ScopeMatch == nil {
  201. return fmt.Errorf("Cannot add rule without a scope match: %v", rule.Name)
  202. }
  203. // Add rule to the index for all kind matches
  204. for _, kindMatch := range rule.KindMatch {
  205. ri.addRuleAtLevel(rule, strings.Split(kindMatch, RuleKindSeparator))
  206. ri.count++
  207. }
  208. return nil
  209. }
  210. /*
  211. addRuleAtLevel adds a new rule to the index at a specific level. The
  212. level is described by a part of the rule kind match.
  213. */
  214. func (ri *RuleIndexKind) addRuleAtLevel(rule *Rule, kindMatchLevel []string) {
  215. var indexType string
  216. var index ruleSubIndex
  217. var ruleSubIndexList []ruleSubIndex
  218. var ok bool
  219. // Pick the right index type
  220. if len(kindMatchLevel) == 1 {
  221. if rule.StateMatch != nil {
  222. indexType = typeRuleIndexState
  223. } else {
  224. indexType = typeRuleIndexAll
  225. }
  226. } else {
  227. indexType = typeRuleIndexKind
  228. }
  229. // Get (create when necessary) a sub index of a specific type for the
  230. // match item of this level
  231. matchItem := kindMatchLevel[0]
  232. // Select the correct ruleSubIndexList
  233. if matchItem == RuleKindWildcard {
  234. ruleSubIndexList = ri.kindAllMatch
  235. } else {
  236. if ruleSubIndexList, ok = ri.kindSingleMatch[matchItem]; !ok {
  237. ruleSubIndexList = make([]ruleSubIndex, 0)
  238. ri.kindSingleMatch[matchItem] = ruleSubIndexList
  239. }
  240. }
  241. // Check if the required index is already existing
  242. for _, item := range ruleSubIndexList {
  243. if item.Type() == indexType {
  244. index = item
  245. break
  246. }
  247. }
  248. // Create a new index if no index was found
  249. if index == nil {
  250. switch indexType {
  251. case typeRuleIndexState:
  252. index = newRuleIndexState()
  253. case typeRuleIndexAll:
  254. index = newRuleIndexAll()
  255. case typeRuleIndexKind:
  256. index = newRuleIndexKind()
  257. }
  258. // Add the new index to the correct list
  259. if matchItem == RuleKindWildcard {
  260. ri.kindAllMatch = append(ruleSubIndexList, index)
  261. } else {
  262. ri.kindSingleMatch[matchItem] = append(ruleSubIndexList, index)
  263. }
  264. }
  265. // Recurse into the next level
  266. index.addRuleAtLevel(rule, kindMatchLevel[1:])
  267. }
  268. /*
  269. IsTriggering checks if a given event triggers a rule in this index.
  270. */
  271. func (ri *RuleIndexKind) IsTriggering(event *Event) bool {
  272. return ri.isTriggeringAtLevel(event, 0)
  273. }
  274. /*
  275. isTriggeringAtLevel checks if a given event triggers a rule at the given
  276. level of the index.
  277. */
  278. func (ri *RuleIndexKind) isTriggeringAtLevel(event *Event, level int) bool {
  279. // Check if the event kind is too general (e.g. rule is defined as a.b.c
  280. // and the event kind is a.b)
  281. if len(event.kind) <= level {
  282. return false
  283. }
  284. levelKind := event.kind[level]
  285. nextLevel := level + 1
  286. // Check rules targeting all events
  287. for _, index := range ri.kindAllMatch {
  288. if index.isTriggeringAtLevel(event, nextLevel) {
  289. return true
  290. }
  291. }
  292. // Check rules targeting specific events
  293. if ruleSubIndexList, ok := ri.kindSingleMatch[levelKind]; ok {
  294. for _, index := range ruleSubIndexList {
  295. if index.isTriggeringAtLevel(event, nextLevel) {
  296. return true
  297. }
  298. }
  299. }
  300. return false
  301. }
  302. /*
  303. Match returns all rules in this index which match a given event. This method
  304. does a full matching check including state matching.
  305. */
  306. func (ri *RuleIndexKind) Match(event *Event) []*Rule {
  307. return ri.matchAtLevel(event, 0)
  308. }
  309. /*
  310. matchAtLevel returns all rules in this index which match a given event
  311. at the given level. This method does a full matching check including
  312. state matching.
  313. */
  314. func (ri *RuleIndexKind) matchAtLevel(event *Event, level int) []*Rule {
  315. // Check if the event kind is too general (e.g. rule is defined as a.b.c
  316. // and the event kind is a.b)
  317. if len(event.kind) <= level {
  318. return nil
  319. }
  320. var ret []*Rule
  321. levelKind := event.kind[level]
  322. nextLevel := level + 1
  323. // Check rules targeting all events
  324. for _, index := range ri.kindAllMatch {
  325. ret = append(ret, index.matchAtLevel(event, nextLevel)...)
  326. }
  327. // Check rules targeting specific events
  328. if ruleSubIndexList, ok := ri.kindSingleMatch[levelKind]; ok {
  329. for _, index := range ruleSubIndexList {
  330. ret = append(ret, index.matchAtLevel(event, nextLevel)...)
  331. }
  332. }
  333. return ret
  334. }
  335. /*
  336. String returns a string representation of this rule index and all subindexes.
  337. */
  338. func (ri *RuleIndexKind) String() string {
  339. return ri.stringIndent("")
  340. }
  341. /*
  342. stringIndent returns a string representation with a given indentation of this
  343. rule index and all subindexes.
  344. */
  345. func (ri *RuleIndexKind) stringIndent(indent string) string {
  346. var buf bytes.Buffer
  347. newIndent := indent + " "
  348. writeIndexList := func(name string, indexList []ruleSubIndex) {
  349. if len(indexList) > 0 {
  350. buf.WriteString(fmt.Sprint(indent, name))
  351. buf.WriteString(fmt.Sprintf(" - %v (%v)\n", ri.Type(), ri.id))
  352. for _, index := range indexList {
  353. buf.WriteString(index.stringIndent(newIndent))
  354. }
  355. }
  356. }
  357. writeIndexList("*", ri.kindAllMatch)
  358. var keys []string
  359. for k := range ri.kindSingleMatch {
  360. keys = append(keys, k)
  361. }
  362. sort.Strings(keys)
  363. for _, key := range keys {
  364. indexList := ri.kindSingleMatch[key]
  365. writeIndexList(key, indexList)
  366. }
  367. return buf.String()
  368. }
  369. // Rule Index State
  370. // ================
  371. /*
  372. RuleMatcherKey is used for pure key - value state matches.
  373. */
  374. type RuleMatcherKey struct {
  375. bits uint64
  376. bitsAny uint64
  377. bitsValue map[interface{}]uint64
  378. bitsRegexes map[uint64]*regexp.Regexp
  379. }
  380. /*
  381. addRule adds a new rule to this key matcher.
  382. */
  383. func (rm *RuleMatcherKey) addRule(num uint, bit uint64, key string, value interface{}) {
  384. // Register rule bit
  385. rm.bits |= bit
  386. if value == nil {
  387. rm.bitsAny |= bit
  388. } else if regex, ok := value.(*regexp.Regexp); ok {
  389. // For regex match we add a bit to the any mask so the presence of
  390. // the key is checked before the actual regex is checked
  391. rm.bitsAny |= bit
  392. rm.bitsRegexes[bit] = regex
  393. } else {
  394. rm.bitsValue[value] |= bit
  395. }
  396. }
  397. /*
  398. match adds matching rules to a given bit mask.
  399. */
  400. func (rm *RuleMatcherKey) match(bits uint64, value interface{}) uint64 {
  401. toRemove := rm.bitsAny ^ rm.bits
  402. if value != nil {
  403. if additionalBits, ok := rm.bitsValue[value]; ok {
  404. toRemove = rm.bitsAny | additionalBits ^ rm.bits
  405. }
  406. }
  407. keyMatchedBits := bits ^ (bits & toRemove)
  408. for bm, r := range rm.bitsRegexes {
  409. if keyMatchedBits&bm > 0 && !r.MatchString(fmt.Sprint(value)) {
  410. // Regex does not match remove the bit
  411. keyMatchedBits ^= keyMatchedBits & bm
  412. }
  413. }
  414. return keyMatchedBits
  415. }
  416. /*
  417. unmatch removes all registered rules in this
  418. */
  419. func (rm *RuleMatcherKey) unmatch(bits uint64) uint64 {
  420. return bits ^ (bits & rm.bits)
  421. }
  422. /*
  423. String returns a string representation of this key matcher.
  424. */
  425. func (rm *RuleMatcherKey) String() string {
  426. var buf bytes.Buffer
  427. buf.WriteString(fmt.Sprintf("%08X *:%08X", rm.bits, rm.bitsAny))
  428. buf.WriteString(" [")
  429. var keys []interface{}
  430. for k := range rm.bitsValue {
  431. keys = append(keys, k)
  432. }
  433. sortutil.InterfaceStrings(keys)
  434. for _, k := range keys {
  435. m := rm.bitsValue[k]
  436. buf.WriteString(fmt.Sprintf("%v:%08X ", k, m))
  437. }
  438. buf.WriteString("] [")
  439. var rkeys []uint64
  440. for k := range rm.bitsRegexes {
  441. rkeys = append(rkeys, k)
  442. }
  443. sortutil.UInt64s(rkeys)
  444. for _, k := range rkeys {
  445. r := rm.bitsRegexes[k]
  446. buf.WriteString(fmt.Sprintf("%08X:%v ", k, r))
  447. }
  448. buf.WriteString("]")
  449. return buf.String()
  450. }
  451. /*
  452. RuleIndexState data structure
  453. */
  454. type RuleIndexState struct {
  455. id uint64 // Id of this rule index
  456. rules []*Rule // All rules stored in this index
  457. keyMap map[string]*RuleMatcherKey // Map of keys (key or key and value) to KeyMatcher
  458. }
  459. /*
  460. newRuleIndexState creates a new rule index matching on event state.
  461. */
  462. func newRuleIndexState() *RuleIndexState {
  463. return &RuleIndexState{newRuleIndexID(), make([]*Rule, 0),
  464. make(map[string]*RuleMatcherKey)}
  465. }
  466. /*
  467. Type returns the type of the rule sub index.
  468. */
  469. func (ri *RuleIndexState) Type() string {
  470. return typeRuleIndexState
  471. }
  472. /*
  473. addRuleAtLevel adds a new rule to the index at a specific level. The
  474. level is described by a part of the rule kind match.
  475. */
  476. func (ri *RuleIndexState) addRuleAtLevel(rule *Rule, kindMatchLevel []string) {
  477. errorutil.AssertTrue(len(kindMatchLevel) == 0,
  478. fmt.Sprint("RuleIndexState must be a leaf - level is:", kindMatchLevel))
  479. num := uint(len(ri.rules))
  480. var bit uint64 = 1 << num
  481. ri.rules = append(ri.rules, rule)
  482. for k, v := range rule.StateMatch {
  483. var ok bool
  484. var keyMatcher *RuleMatcherKey
  485. if keyMatcher, ok = ri.keyMap[k]; !ok {
  486. keyMatcher = &RuleMatcherKey{0, 0, make(map[interface{}]uint64), make(map[uint64]*regexp.Regexp)}
  487. ri.keyMap[k] = keyMatcher
  488. }
  489. keyMatcher.addRule(num, bit, k, v)
  490. }
  491. }
  492. /*
  493. isTriggeringAtLevel checks if a given event triggers a rule at the given
  494. level of the index.
  495. */
  496. func (ri *RuleIndexState) isTriggeringAtLevel(event *Event, level int) bool {
  497. return len(event.kind) == level
  498. }
  499. /*
  500. matchAtLevel returns all rules in this index which match a given event
  501. at the given level. This method does a full matching check including
  502. state matching.
  503. */
  504. func (ri *RuleIndexState) matchAtLevel(event *Event, level int) []*Rule {
  505. if len(event.kind) != level {
  506. return nil
  507. }
  508. // Assume all rules match and remove the ones with don't
  509. var matchBits uint64 = (1 << uint(len(ri.rules))) - 1
  510. // Match key and values
  511. for key, matcher := range ri.keyMap {
  512. if val, ok := event.state[key]; ok {
  513. // Key is present in event
  514. matchBits = matcher.match(matchBits, val)
  515. } else {
  516. // Key is not present in event - remove all rules which require the key
  517. matchBits = matcher.unmatch(matchBits)
  518. }
  519. if matchBits == 0 {
  520. // All rules have been excluded
  521. return nil
  522. }
  523. }
  524. var ret []*Rule
  525. var collectionBits uint64 = 1
  526. // Collect matched rules
  527. for i := 0; collectionBits <= matchBits; i++ {
  528. if matchBits&collectionBits > 0 {
  529. ret = append(ret, ri.rules[i])
  530. }
  531. collectionBits <<= 1
  532. }
  533. return ret
  534. }
  535. /*
  536. stringIndent returns a string representation with a given indentation of this
  537. rule index and all subindexes.
  538. */
  539. func (ri *RuleIndexState) stringIndent(indent string) string {
  540. var buf bytes.Buffer
  541. buf.WriteString(fmt.Sprintf("%v%v (%v) ", indent, ri.Type(), ri.id))
  542. buf.WriteString("[")
  543. for _, r := range ri.rules {
  544. buf.WriteString(fmt.Sprintf("%v ", r.Name))
  545. }
  546. buf.WriteString("]\n")
  547. newIndent := indent + " "
  548. var keys []string
  549. for k := range ri.keyMap {
  550. keys = append(keys, k)
  551. }
  552. sort.Strings(keys)
  553. for _, k := range keys {
  554. m := ri.keyMap[k]
  555. buf.WriteString(fmt.Sprintf("%v%v - %v\n", newIndent, k, m))
  556. }
  557. return buf.String()
  558. }
  559. // Rule Index All
  560. // ==============
  561. /*
  562. RuleIndexAll data structure.
  563. */
  564. type RuleIndexAll struct {
  565. id uint64 // Id of this rule index
  566. rules []*Rule // Rules with target all events of a specific category
  567. }
  568. /*
  569. newRuleIndexAll creates a new leaf rule index matching on all events.
  570. */
  571. func newRuleIndexAll() *RuleIndexAll {
  572. return &RuleIndexAll{newRuleIndexID(), make([]*Rule, 0)}
  573. }
  574. /*
  575. Type returns the type of the rule sub index.
  576. */
  577. func (ri *RuleIndexAll) Type() string {
  578. return typeRuleIndexAll
  579. }
  580. /*
  581. addRuleAtLevel adds a new rule to the index at a specific level. The
  582. level is described by a part of the rule kind match.
  583. */
  584. func (ri *RuleIndexAll) addRuleAtLevel(rule *Rule, kindMatchLevel []string) {
  585. ri.rules = append(ri.rules, rule)
  586. }
  587. /*
  588. isTriggeringAtLevel checks if a given event triggers a rule at the given
  589. level of the index.
  590. */
  591. func (ri *RuleIndexAll) isTriggeringAtLevel(event *Event, level int) bool {
  592. return len(event.kind) == level
  593. }
  594. /*
  595. matchAtLevel returns all rules in this index which match a given event
  596. at the given level. This method does a full matching check including
  597. state matching.
  598. */
  599. func (ri *RuleIndexAll) matchAtLevel(event *Event, level int) []*Rule {
  600. if len(event.kind) != level {
  601. return nil
  602. }
  603. return ri.rules
  604. }
  605. /*
  606. stringIndent returns a string representation with a given indentation of this
  607. rule index and all subindexes.
  608. */
  609. func (ri *RuleIndexAll) stringIndent(indent string) string {
  610. var buf bytes.Buffer
  611. buf.WriteString(fmt.Sprintf("%v%v (%v)\n", indent, ri.Type(), ri.id))
  612. newIndent := indent + " "
  613. for _, rule := range ri.rules {
  614. buf.WriteString(fmt.Sprintf("%v%v\n", newIndent, rule))
  615. }
  616. return buf.String()
  617. }
  618. // Unique id creation
  619. // ==================
  620. var ruleindexidcounter uint64 = 1
  621. var ruleindexidcounterLock = &sync.Mutex{}
  622. /*
  623. newId returns a new unique id.
  624. */
  625. func newRuleIndexID() uint64 {
  626. ruleindexidcounterLock.Lock()
  627. defer ruleindexidcounterLock.Unlock()
  628. ret := ruleindexidcounter
  629. ruleindexidcounter++
  630. return ret
  631. }