|
@ -7,19 +7,12 @@ import ( |
|
|
"os" |
|
|
"os" |
|
|
"os/signal" |
|
|
"os/signal" |
|
|
"strings" |
|
|
"strings" |
|
|
"sync" |
|
|
|
|
|
"syscall" |
|
|
|
|
|
"time" |
|
|
|
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
var ( |
|
|
var ( |
|
|
GoPath = os.Getenv("GOPATH") |
|
|
GoPath = os.Getenv("GOPATH") |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
func init() { |
|
|
|
|
|
initAFSIGHUPWatcher() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func TrapSignal(cb func()) { |
|
|
func TrapSignal(cb func()) { |
|
|
c := make(chan os.Signal, 1) |
|
|
c := make(chan os.Signal, 1) |
|
|
signal.Notify(c, os.Interrupt) |
|
|
signal.Notify(c, os.Interrupt) |
|
@ -112,153 +105,6 @@ func WriteFileAtomic(filePath string, newBytes []byte, mode os.FileMode) error { |
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------------
|
|
|
//--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
/* AutoFile usage |
|
|
|
|
|
|
|
|
|
|
|
// Create/Append to ./autofile_test
|
|
|
|
|
|
af, err := OpenAutoFile("autofile_test") |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
panic(err) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Stream of writes.
|
|
|
|
|
|
// During this time, the file may be moved e.g. by logRotate.
|
|
|
|
|
|
for i := 0; i < 60; i++ { |
|
|
|
|
|
af.Write([]byte(Fmt("LOOP(%v)", i))) |
|
|
|
|
|
time.Sleep(time.Second) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Close the AutoFile
|
|
|
|
|
|
err = af.Close() |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
panic(err) |
|
|
|
|
|
} |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
const autoFileOpenDuration = 1000 * time.Millisecond |
|
|
|
|
|
|
|
|
|
|
|
// Automatically closes and re-opens file for writing.
|
|
|
|
|
|
// This is useful for using a log file with the logrotate tool.
|
|
|
|
|
|
type AutoFile struct { |
|
|
|
|
|
ID string |
|
|
|
|
|
Path string |
|
|
|
|
|
ticker *time.Ticker |
|
|
|
|
|
mtx sync.Mutex |
|
|
|
|
|
file *os.File |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func OpenAutoFile(path string) (af *AutoFile, err error) { |
|
|
|
|
|
af = &AutoFile{ |
|
|
|
|
|
ID: RandStr(12) + ":" + path, |
|
|
|
|
|
Path: path, |
|
|
|
|
|
ticker: time.NewTicker(autoFileOpenDuration), |
|
|
|
|
|
} |
|
|
|
|
|
if err = af.openFile(); err != nil { |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
go af.processTicks() |
|
|
|
|
|
autoFileWatchers.addAutoFile(af) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (af *AutoFile) Close() error { |
|
|
|
|
|
af.ticker.Stop() |
|
|
|
|
|
err := af.closeFile() |
|
|
|
|
|
autoFileWatchers.removeAutoFile(af) |
|
|
|
|
|
return err |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (af *AutoFile) processTicks() { |
|
|
|
|
|
for { |
|
|
|
|
|
_, ok := <-af.ticker.C |
|
|
|
|
|
if !ok { |
|
|
|
|
|
return // Done.
|
|
|
|
|
|
} |
|
|
|
|
|
af.closeFile() |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (af *AutoFile) closeFile() (err error) { |
|
|
|
|
|
af.mtx.Lock() |
|
|
|
|
|
defer af.mtx.Unlock() |
|
|
|
|
|
|
|
|
|
|
|
file := af.file |
|
|
|
|
|
if file == nil { |
|
|
|
|
|
return nil |
|
|
|
|
|
} |
|
|
|
|
|
af.file = nil |
|
|
|
|
|
return file.Close() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (af *AutoFile) Write(b []byte) (n int, err error) { |
|
|
|
|
|
af.mtx.Lock() |
|
|
|
|
|
defer af.mtx.Unlock() |
|
|
|
|
|
if af.file == nil { |
|
|
|
|
|
if err = af.openFile(); err != nil { |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
return af.file.Write(b) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (af *AutoFile) openFile() error { |
|
|
|
|
|
file, err := os.OpenFile(af.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return err |
|
|
|
|
|
} |
|
|
|
|
|
af.file = file |
|
|
|
|
|
return nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
var autoFileWatchers *afSIGHUPWatcher |
|
|
|
|
|
|
|
|
|
|
|
func initAFSIGHUPWatcher() { |
|
|
|
|
|
autoFileWatchers = newAFSIGHUPWatcher() |
|
|
|
|
|
|
|
|
|
|
|
c := make(chan os.Signal, 1) |
|
|
|
|
|
signal.Notify(c, syscall.SIGHUP) |
|
|
|
|
|
|
|
|
|
|
|
go func() { |
|
|
|
|
|
for _ = range c { |
|
|
|
|
|
autoFileWatchers.closeAll() |
|
|
|
|
|
} |
|
|
|
|
|
}() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type afSIGHUPWatcher struct { |
|
|
|
|
|
mtx sync.Mutex |
|
|
|
|
|
autoFiles map[string]*AutoFile |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func newAFSIGHUPWatcher() *afSIGHUPWatcher { |
|
|
|
|
|
return &afSIGHUPWatcher{ |
|
|
|
|
|
autoFiles: make(map[string]*AutoFile, 10), |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (afw *afSIGHUPWatcher) addAutoFile(af *AutoFile) { |
|
|
|
|
|
afw.mtx.Lock() |
|
|
|
|
|
afw.autoFiles[af.ID] = af |
|
|
|
|
|
afw.mtx.Unlock() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (afw *afSIGHUPWatcher) removeAutoFile(af *AutoFile) { |
|
|
|
|
|
afw.mtx.Lock() |
|
|
|
|
|
delete(afw.autoFiles, af.ID) |
|
|
|
|
|
afw.mtx.Unlock() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (afw *afSIGHUPWatcher) closeAll() { |
|
|
|
|
|
afw.mtx.Lock() |
|
|
|
|
|
for _, af := range afw.autoFiles { |
|
|
|
|
|
af.closeFile() |
|
|
|
|
|
} |
|
|
|
|
|
afw.mtx.Unlock() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
func Tempfile(prefix string) (*os.File, string) { |
|
|
func Tempfile(prefix string) (*os.File, string) { |
|
|
file, err := ioutil.TempFile("", prefix) |
|
|
file, err := ioutil.TempFile("", prefix) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|