mirror of https://github.com/hak5/overseer.git
101 lines
1.9 KiB
Go
101 lines
1.9 KiB
Go
package fetcher
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// File checks the provided Path, at the provided
|
|
// Interval for new Go binaries. When a new binary
|
|
// is found it will replace the currently running
|
|
// binary.
|
|
type File struct {
|
|
Path string
|
|
Interval time.Duration
|
|
// hash is the file modify time and its size
|
|
hash string
|
|
delay bool
|
|
}
|
|
|
|
// Init sets the Path and Interval options
|
|
func (f *File) Init() error {
|
|
if f.Path == "" {
|
|
return fmt.Errorf("Path required")
|
|
}
|
|
if f.Interval < 1*time.Second {
|
|
f.Interval = 1 * time.Second
|
|
}
|
|
if err := f.updateHash(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Fetch file from the specified Path
|
|
func (f *File) Fetch() (io.Reader, error) {
|
|
//only delay after first fetch
|
|
if f.delay {
|
|
time.Sleep(f.Interval)
|
|
}
|
|
f.delay = true
|
|
lastHash := f.hash
|
|
if err := f.updateHash(); err != nil {
|
|
return nil, err
|
|
}
|
|
// no change
|
|
if lastHash == f.hash {
|
|
return nil, nil
|
|
}
|
|
// changed!
|
|
file, err := os.Open(f.Path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
//check every 1/4s for 5s to
|
|
//ensure its not mid-copy
|
|
const rate = 250 * time.Millisecond
|
|
const total = int(5 * time.Second / rate)
|
|
attempt := 1
|
|
for {
|
|
if attempt == total {
|
|
file.Close()
|
|
return nil, errors.New("file is currently being changed")
|
|
}
|
|
attempt++
|
|
//sleep
|
|
time.Sleep(rate)
|
|
//check hash!
|
|
if err := f.updateHash(); err != nil {
|
|
file.Close()
|
|
return nil, err
|
|
}
|
|
//check until no longer changing
|
|
if lastHash == f.hash {
|
|
break
|
|
}
|
|
lastHash = f.hash
|
|
}
|
|
return file, nil
|
|
}
|
|
|
|
func (f *File) updateHash() error {
|
|
file, err := os.Open(f.Path)
|
|
if err != nil {
|
|
//binary does not exist, skip
|
|
if os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("Open file error: %s", err)
|
|
}
|
|
defer file.Close()
|
|
s, err := file.Stat()
|
|
if err != nil {
|
|
return fmt.Errorf("Get file stat error: %s", err)
|
|
}
|
|
f.hash = fmt.Sprintf("%d|%d", s.ModTime().UnixNano(), s.Size())
|
|
return nil
|
|
}
|