diff --git a/config.go b/config.go new file mode 100644 index 0000000..84e42fd --- /dev/null +++ b/config.go @@ -0,0 +1,233 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +type AlrmConfig struct { + Groups []*AlrmGroup +} + +func (ac *AlrmConfig) NewGroup() *AlrmGroup { + group := &AlrmGroup{} + ac.Groups = append(ac.Groups, group) + return group +} + +func (ac *AlrmConfig) LastGroup() *AlrmGroup { + return ac.Groups[len(ac.Groups)-1] +} + +type AlrmGroup struct { + Name string + Hosts []*AlrmHost +} + +func (ag *AlrmGroup) NewHost() *AlrmHost { + host := &AlrmHost{} + ag.Hosts = append(ag.Hosts, host) + return host +} + +func (ag *AlrmGroup) LastHost() *AlrmHost { + return ag.Hosts[len(ag.Hosts)-1] +} + +type AlrmHost struct { + Name string + Address string +} + +const ( + TK_NONE = iota + TK_MONITOR + TK_GROUP + TK_PLAIN_HOST + TK_GROUP_HOST +) + +func stateName(s int) string { + switch s { + case TK_NONE: + return "TK_NONE" + case TK_MONITOR: + return "TK_MONTIOR" + case TK_GROUP: + return "TK_GROUP" + case TK_PLAIN_HOST: + return "TK_PLAIN_HOST" + case TK_GROUP_HOST: + return "TK_GROUP_HOST" + default: + return "UNKNOWN" + } +} + +func ReadConfig(fn string) (*AlrmConfig, error) { + file, err := os.Open(fn) + if err != nil { + return nil, fmt.Errorf("cannot open config \"%s\": %s", fn, err.Error()) + } + defer file.Close() + + config := &AlrmConfig{} + parser := &Parser{} + + scan := bufio.NewScanner(file) + scan.Split(parser.Split) + for scan.Scan() { + tk := scan.Text() + + stateswitch: + switch parser.State { + case TK_NONE: + switch strings.ToLower(tk) { + case "monitor": + parser.SetState(TK_MONITOR) + default: + return nil, fmt.Errorf("Invalid token in %s, line %d: \"%s\"", + fn, parser.Line, tk) + } + + case TK_MONITOR: + switch strings.ToLower(tk) { + case "host": + config.NewGroup().NewHost() + parser.SetState(TK_PLAIN_HOST) + + case "group": + config.NewGroup() + parser.SetState(TK_GROUP) + + default: + parser.SetState(TK_NONE) + goto stateswitch + } + + case TK_GROUP: + group := config.LastGroup() + + switch strings.ToLower(tk) { + case "host": + group.NewHost() + parser.SetState(TK_GROUP_HOST) + continue + + default: + if group.Name == "" { + group.Name = tk + continue + } + + parser.SetState(TK_MONITOR) + goto stateswitch + } + + case TK_PLAIN_HOST: + fallthrough + case TK_GROUP_HOST: + host := config.LastGroup().LastHost() + if host.Name == "" { + host.Name = tk + continue + } + + switch strings.ToLower(tk) { + case "address": + if scan.Scan() { + host.Address = scan.Text() + } + continue + + default: + if parser.State == TK_GROUP_HOST { + parser.SetState(TK_GROUP) + } else if parser.State == TK_PLAIN_HOST { + parser.SetState(TK_MONITOR) + } + + goto stateswitch + } + + default: + return nil, fmt.Errorf("Unknown parser state: %d", parser.State) + } + } + if err := scan.Err(); err != nil { + return nil, err + } + return config, nil +} + +type Parser struct { + Line int + State int +} + +func (pr *Parser) SetState(state int) { + //fmt.Printf("%s -> %s\n", stateName(pr.State), stateName(state)) + pr.State = state +} + +func (pr *Parser) Split(data []byte, atEOF bool) (int, []byte, error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + + var ignoreline bool + var started bool + var startidx int + var quote byte + + for i := 0; i < len(data); i++ { + c := data[i] + switch c { + case '\f', '\n', '\r': + pr.Line++ + if ignoreline { + ignoreline = false + continue + } + fallthrough + + case ' ', '\t', '\v': + if started && quote == 0 { + return i + 1, data[startidx:i], nil + } + + case '\'', '"', '`': + if started && quote == c { + return i + 1, data[startidx:i], nil + } + + if quote == 0 { + quote = c + } + + case '#': + if !started { + ignoreline = true + } + + default: + if !ignoreline && !started { + started = true + startidx = i + } + } + } + + if atEOF { + if ignoreline { + return len(data), nil, nil + } + if started { + return len(data), data[startidx:], nil + } + } + + return 0, nil, nil +} diff --git a/main.go b/main.go index c6e99cd..142cbb5 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,7 @@ package main import ( - "bufio" + "encoding/json" "fmt" "os" ) @@ -12,84 +12,17 @@ func main() { os.Exit(1) } - file, err := os.Open(os.Args[1]) + config, err := ReadConfig(os.Args[1]) if err != nil { - fmt.Fprintf(os.Stderr, "cannot open %s: %s\n", - os.Args[1], err.Error()) + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) os.Exit(1) } - defer file.Close() - split := &Splitter{} - scan := bufio.NewScanner(file) - scan.Split(split.Split) - for lastline := 0; scan.Scan(); { - if lastline < split.Line { - lastline = split.Line - fmt.Printf("\n") - } - word := scan.Text() - fmt.Printf("[%s] ", word) - } - fmt.Printf("\n") -} - - -type Splitter struct { - Line int -} - -func (sp *Splitter) Split(data []byte, atEOF bool) (int, []byte, error) { - var ignoreline bool - var started bool - var startidx int - var quote byte - - for i := 0; i < len(data); i++ { - c := data[i] - switch c { - case '\f', '\n', '\r': - sp.Line++ - if ignoreline { - return i + 1, nil, nil - } - fallthrough - - case ' ', '\t', '\v': - if started && quote == 0 { - return i + 1, data[startidx:i], nil - } - - case '\'', '"', '`': - if started && quote == c { - return i + 1, data[startidx:i], nil - } - - if quote == 0 { - quote = c - } - - case '#': - if !started { - ignoreline = true - } - - default: - if !ignoreline && !started { - started = true - startidx = i - } - } - } - - if atEOF { - if ignoreline { - return len(data), nil, nil - } - if started { - return len(data), data[startidx:], nil - } + o, err := json.Marshal(config) + if err != nil { + fmt.Fprintf(os.Stderr, "JSON error: %s\n", err.Error()) + os.Exit(1) } - return 0, nil, nil + fmt.Println(string(o)) } diff --git a/small.config b/small.config index f7b2492..c98c526 100644 --- a/small.config +++ b/small.config @@ -1,2 +1,10 @@ - # Address is optional, derived from host +# Address is optional, derived from host monitor host monkey.com + +monitor host stupid.com + +monitor group webservers + # Monitor host one + host one.com address 10.12.121.1 + # Monitor host two + host two.com address