diff --git a/cmd/mothd/common.go b/cmd/mothd/common.go index 9da809b..2217258 100644 --- a/cmd/mothd/common.go +++ b/cmd/mothd/common.go @@ -25,4 +25,4 @@ func (c *Component) path(parts ...string) string { func (c *Component) Run(updateInterval time.Duration) { // Stub! -} \ No newline at end of file +} diff --git a/cmd/mothd/main.go b/cmd/mothd/main.go index 5aa11d3..04394bf 100644 --- a/cmd/mothd/main.go +++ b/cmd/mothd/main.go @@ -1,24 +1,50 @@ package main import ( - "time" + "github.com/namsral/flag" "log" + "time" ) func main() { log.Print("Started") - - theme := NewTheme("../../theme") - state := NewState("../../state") - puzzles := NewMothballs("../../mothballs") - - - interval := 2 * time.Second - go theme.Run(interval) - go state.Run(interval) - go puzzles.Run(interval) - + + themePath := flag.String( + "theme", + "theme", + "Path to theme files", + ) + statePath := flag.String( + "state", + "state", + "Path to state files", + ) + puzzlePath := flag.String( + "mothballs", + "mothballs", + "Path to mothballs to host", + ) + refreshInterval := flag.Duration( + "refresh", + 2*time.Second, + "Duration between maintenance tasks", + ) + bindStr := flag.String( + "bind", + ":8000", + "Bind [host]:port for HTTP service", + ) + + theme := NewTheme(*themePath) + state := NewState(*statePath) + puzzles := NewMothballs(*puzzlePath) + + go theme.Run(*refreshInterval) + go state.Run(*refreshInterval) + go puzzles.Run(*refreshInterval) + + log.Println("I would be binding to", *bindStr) time.Sleep(1 * time.Second) log.Print(state.Export("")) time.Sleep(19 * time.Second) -} \ No newline at end of file +} diff --git a/cmd/mothd/mothballs.go b/cmd/mothd/mothballs.go index 08ee41c..1ceaac6 100644 --- a/cmd/mothd/mothballs.go +++ b/cmd/mothd/mothballs.go @@ -1,10 +1,10 @@ package main import ( - "time" "io/ioutil" - "strings" "log" + "strings" + "time" ) type Mothballs struct { diff --git a/cmd/mothd/state.go b/cmd/mothd/state.go index ff3bb62..95580a8 100644 --- a/cmd/mothd/state.go +++ b/cmd/mothd/state.go @@ -34,6 +34,7 @@ type StateExport struct { // The only thing State methods need to know is the path to the state directory. type State struct { Component + Enabled bool update chan bool } @@ -42,32 +43,60 @@ func NewState(baseDir string) *State { Component: Component{ baseDir: baseDir, }, + Enabled: true, update: make(chan bool, 10), } } // Check a few things to see if this state directory is "enabled". -func (s *State) Enabled() bool { +func (s *State) UpdateEnabled() { if _, err := os.Stat(s.path("enabled")); os.IsNotExist(err) { + s.Enabled = false log.Print("Suspended: enabled file missing") - return false + return } - untilspec, err := ioutil.ReadFile(s.path("until")) - if err == nil { - untilspecs := strings.TrimSpace(string(untilspec)) - until, err := time.Parse(time.RFC3339, untilspecs) + nextEnabled := true + untilFile, err := os.Open(s.path("hours")) + if err != nil { + return + } + defer untilFile.Close() + + scanner := bufio.NewScanner(untilFile) + for scanner.Scan() { + line := scanner.Text() + if len(line) < 1 { + continue + } + + thisEnabled := true + switch line[0] { + case '+': + thisEnabled = true + line = line[1:] + case '-': + thisEnabled = false + line = line[1:] + case '#': + continue + default: + log.Printf("Misformatted line in hours file") + } + line = strings.TrimSpace(line) + until, err := time.Parse(time.RFC3339, line) if err != nil { - log.Printf("Suspended: Unparseable until date: %s", untilspec) - return false + log.Printf("Suspended: Unparseable until date: %s", line) + continue } if until.Before(time.Now()) { - log.Print("Suspended: until time reached, suspending maintenance") - return false + nextEnabled = thisEnabled } } - - return true + if nextEnabled != s.Enabled { + s.Enabled = nextEnabled + log.Println("Setting enabled to", s.Enabled, "based on hours file") + } } // Returns team name given a team ID. @@ -291,17 +320,23 @@ func (s *State) maybeInitialize() { // Create some files ioutil.WriteFile( s.path("initialized"), - []byte("Remove this file to re-initialized the contest\n"), + []byte("state/initialized: remove to re-initialize the contest\n"), 0644, ) ioutil.WriteFile( s.path("enabled"), - []byte("Remove this file to suspend the contest\n"), + []byte("state/enabled: remove to suspend the contest\n"), 0644, ) ioutil.WriteFile( - s.path("until"), - []byte("3009-10-31T00:00:00Z\n"), + s.path("hours"), + []byte( + "# state/hours: when the contest is enabled\n"+ + "# Lines starting with + enable, with - disable.\n"+ + "\n"+ + "+ 1970-01-01T00:00:00Z\n"+ + "- 3019-10-31T00:00:00Z\n", + ), 0644, ) ioutil.WriteFile( @@ -319,10 +354,12 @@ func (s *State) maybeInitialize() { func (s *State) Run(updateInterval time.Duration) { for { s.maybeInitialize() - if s.Enabled() { + s.UpdateEnabled() + if s.Enabled { s.collectPoints() } + // Wait for something to happen select { case <-s.update: case <-time.After(updateInterval):