Files
mgmt/recwatch/configwatch.go
James Shubin 12fce52cd7 legal: Happy 2018 everyone...
Done with:

ack '2017+' -l | xargs sed -i -e 's/2017+/2018+/g'

Checked manually with:

git add -p

Hello to future James from 2019, and Happy Hacking!
2018-01-03 21:22:07 -05:00

149 lines
3.6 KiB
Go

// Mgmt
// Copyright (C) 2013-2018+ James Shubin and the project contributors
// Written by James Shubin <james@shubin.ca> and the project contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package recwatch
import (
"log"
"sync"
)
// ConfigWatcher returns events on a channel anytime one of its files events.
type ConfigWatcher struct {
Flags Flags
ch chan string
wg sync.WaitGroup
closechan chan struct{}
errorchan chan error
}
// NewConfigWatcher creates a new ConfigWatcher struct.
func NewConfigWatcher() *ConfigWatcher {
return &ConfigWatcher{
ch: make(chan string),
closechan: make(chan struct{}),
errorchan: make(chan error),
}
}
// Add new file paths to watch for events on.
func (obj *ConfigWatcher) Add(file ...string) {
if len(file) == 0 {
return
}
if len(file) > 1 {
for _, f := range file { // add all the files...
obj.Add(f) // recurse
}
return
}
// otherwise, add the one file passed in...
obj.wg.Add(1)
go func() {
defer obj.wg.Done()
ch := obj.ConfigWatch(file[0])
for {
select {
case e, ok := <-ch:
if !ok { // channel closed
return
}
if e != nil {
obj.errorchan <- e
return
}
select {
case obj.ch <- file[0]: // send on channel
case <-obj.closechan:
return // never mind, close early!
}
continue
// not needed, closes via ConfigWatch() chan close
//case <-obj.closechan:
// return
}
}
}()
}
// Error returns a channel of errors that notifies us of permanent issues.
func (obj *ConfigWatcher) Error() <-chan error {
return obj.errorchan
}
// Events returns a channel to listen on for file events. It closes when it is
// emptied after the Close() method is called. You can test for closure with the
// f, more := <-obj.Events() pattern.
func (obj *ConfigWatcher) Events() chan string {
return obj.ch
}
// Close shuts down the ConfigWatcher object. It closes the Events channel after
// all the currently pending events have been emptied.
func (obj *ConfigWatcher) Close() {
if obj.ch == nil {
return
}
close(obj.closechan)
obj.wg.Wait() // wait until everyone is done sending on obj.ch
//obj.ch <- "" // send finished message
close(obj.ch)
obj.ch = nil
close(obj.errorchan)
}
// ConfigWatch writes on the channel every time an event is seen for the path.
func (obj *ConfigWatcher) ConfigWatch(file string) chan error {
ch := make(chan error)
go func() {
recWatcher, err := NewRecWatcher(file, false)
if err != nil {
ch <- err
close(ch)
return
}
recWatcher.Flags = obj.Flags
defer recWatcher.Close()
for {
if obj.Flags.Debug {
log.Printf("Watching: %v", file)
}
select {
case event, ok := <-recWatcher.Events():
if !ok { // channel is closed
close(ch)
return
}
if err := event.Error; err != nil {
ch <- err
close(ch)
return
}
select {
case ch <- nil: // send event!
case <-obj.closechan:
close(ch)
return
}
}
}
//close(ch)
}()
return ch
}