github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/osutil/udev/netlink/matcher.go (about)

     1  package netlink
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  )
     7  
     8  type Matcher interface {
     9  	Evaluate(e UEvent) bool
    10  	EvaluateAction(a KObjAction) bool
    11  	EvaluateEnv(e map[string]string) bool
    12  	Compile() error
    13  	String() string
    14  }
    15  
    16  type RuleDefinition struct {
    17  	Action *string           `json:"action,omitempty"`
    18  	Env    map[string]string `json:"env,omitempty"`
    19  	rule   *rule
    20  }
    21  
    22  // Evaluate return true if all condition match uevent and envs in rule exists in uevent
    23  func (r RuleDefinition) Evaluate(e UEvent) bool {
    24  	// Compile if needed
    25  	if r.rule == nil {
    26  		if err := r.Compile(); err != nil {
    27  			return false
    28  		}
    29  	}
    30  
    31  	return r.EvaluateAction(e.Action) && r.EvaluateEnv(e.Env)
    32  }
    33  
    34  // EvaluateAction return true if the action match
    35  func (r RuleDefinition) EvaluateAction(a KObjAction) (match bool) {
    36  	// Compile if needed
    37  	if r.rule == nil {
    38  		if err := r.Compile(); err != nil {
    39  			return false
    40  		}
    41  	}
    42  	if match = (r.rule.Action == nil); !match {
    43  		match = r.rule.Action.MatchString(a.String())
    44  	}
    45  	return
    46  }
    47  
    48  // EvaluateEnv return true if all env match and exists
    49  func (r RuleDefinition) EvaluateEnv(e map[string]string) bool {
    50  	// Compile if needed
    51  	if r.rule == nil {
    52  		if err := r.Compile(); err != nil {
    53  			return false
    54  		}
    55  	}
    56  	return r.rule.Env.Evaluate(e)
    57  }
    58  
    59  // Compile prepare rule definition to be able to Evaluate() an UEvent
    60  func (r *RuleDefinition) Compile() error {
    61  	r.rule = &rule{
    62  		Env: make(map[string]*regexp.Regexp, 0),
    63  	}
    64  
    65  	if r.Action != nil {
    66  		action, err := regexp.Compile(*(r.Action))
    67  		if err != nil {
    68  			return err
    69  		}
    70  		r.rule.Action = action
    71  	}
    72  
    73  	for k, v := range r.Env {
    74  		reg, err := regexp.Compile(v)
    75  		if err != nil {
    76  			return err
    77  		}
    78  		r.rule.Env[k] = reg
    79  	}
    80  	return nil
    81  }
    82  
    83  func (r RuleDefinition) String() string {
    84  	return fmt.Sprintf("Action: %v / Env: %+v", r.Action, r.Env)
    85  }
    86  
    87  // rule is the compiled version of the RuleDefinition
    88  type rule struct {
    89  	Action *regexp.Regexp
    90  	Env    Env
    91  }
    92  
    93  type Env map[string]*regexp.Regexp
    94  
    95  func (e Env) Evaluate(env map[string]string) bool {
    96  	foundEnv := (len(e) == 0)
    97  	for envName, reg := range e {
    98  		foundEnv = false
    99  		for k, v := range env {
   100  			if k == envName {
   101  				foundEnv = true
   102  				if !reg.MatchString(v) {
   103  					return false
   104  				}
   105  			}
   106  		}
   107  		if !foundEnv {
   108  			return false
   109  		}
   110  	}
   111  
   112  	return foundEnv
   113  
   114  }
   115  
   116  // RuleDefinitions is like chained rule with OR operator
   117  type RuleDefinitions struct {
   118  	Rules []RuleDefinition
   119  }
   120  
   121  func (rs *RuleDefinitions) AddRule(r RuleDefinition) {
   122  	rs.Rules = append(rs.Rules, r)
   123  }
   124  
   125  func (rs *RuleDefinitions) Compile() error {
   126  	for _, r := range rs.Rules {
   127  		if err := r.Compile(); err != nil {
   128  			return err
   129  		}
   130  	}
   131  	return nil
   132  }
   133  
   134  func (rs RuleDefinitions) Evaluate(e UEvent) bool {
   135  	for _, r := range rs.Rules {
   136  		if r.Evaluate(e) {
   137  			return true
   138  		}
   139  	}
   140  	return false
   141  }
   142  
   143  // EvaluateAction return true if the action match
   144  func (rs RuleDefinitions) EvaluateAction(a KObjAction) (match bool) {
   145  	for _, r := range rs.Rules {
   146  		if r.EvaluateAction(a) {
   147  			return true
   148  		}
   149  	}
   150  	return false
   151  }
   152  
   153  // EvaluateEnv return true if almost one env match all regexp
   154  func (rs RuleDefinitions) EvaluateEnv(e map[string]string) bool {
   155  	for _, r := range rs.Rules {
   156  		if r.EvaluateEnv(e) {
   157  			return true
   158  		}
   159  	}
   160  	return false
   161  }
   162  
   163  func (rs RuleDefinitions) String() string {
   164  	output := ""
   165  	for _, v := range rs.Rules {
   166  		output += "- " + v.String() + "\n"
   167  	}
   168  	return output
   169  }