|
@ -8,6 +8,7 @@ import ( |
|
|
"os/signal" |
|
|
"os/signal" |
|
|
"strings" |
|
|
"strings" |
|
|
"sync" |
|
|
"sync" |
|
|
|
|
|
"syscall" |
|
|
"time" |
|
|
"time" |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
@ -15,6 +16,10 @@ 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) |
|
@ -134,6 +139,7 @@ const autoFileOpenDuration = 1000 * time.Millisecond |
|
|
// Automatically closes and re-opens file for writing.
|
|
|
// Automatically closes and re-opens file for writing.
|
|
|
// This is useful for using a log file with the logrotate tool.
|
|
|
// This is useful for using a log file with the logrotate tool.
|
|
|
type AutoFile struct { |
|
|
type AutoFile struct { |
|
|
|
|
|
ID string |
|
|
Path string |
|
|
Path string |
|
|
ticker *time.Ticker |
|
|
ticker *time.Ticker |
|
|
mtx sync.Mutex |
|
|
mtx sync.Mutex |
|
@ -142,6 +148,7 @@ type AutoFile struct { |
|
|
|
|
|
|
|
|
func OpenAutoFile(path string) (af *AutoFile, err error) { |
|
|
func OpenAutoFile(path string) (af *AutoFile, err error) { |
|
|
af = &AutoFile{ |
|
|
af = &AutoFile{ |
|
|
|
|
|
ID: RandStr(12) + ":" + path, |
|
|
Path: path, |
|
|
Path: path, |
|
|
ticker: time.NewTicker(autoFileOpenDuration), |
|
|
ticker: time.NewTicker(autoFileOpenDuration), |
|
|
} |
|
|
} |
|
@ -149,14 +156,14 @@ func OpenAutoFile(path string) (af *AutoFile, err error) { |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
go af.processTicks() |
|
|
go af.processTicks() |
|
|
|
|
|
autoFileWatchers.addAutoFile(af) |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func (af *AutoFile) Close() error { |
|
|
func (af *AutoFile) Close() error { |
|
|
af.ticker.Stop() |
|
|
af.ticker.Stop() |
|
|
af.mtx.Lock() |
|
|
|
|
|
err := af.closeFile() |
|
|
err := af.closeFile() |
|
|
af.mtx.Unlock() |
|
|
|
|
|
|
|
|
autoFileWatchers.removeAutoFile(af) |
|
|
return err |
|
|
return err |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -166,13 +173,14 @@ func (af *AutoFile) processTicks() { |
|
|
if !ok { |
|
|
if !ok { |
|
|
return // Done.
|
|
|
return // Done.
|
|
|
} |
|
|
} |
|
|
af.mtx.Lock() |
|
|
|
|
|
af.closeFile() |
|
|
af.closeFile() |
|
|
af.mtx.Unlock() |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func (af *AutoFile) closeFile() (err error) { |
|
|
func (af *AutoFile) closeFile() (err error) { |
|
|
|
|
|
af.mtx.Lock() |
|
|
|
|
|
defer af.mtx.Unlock() |
|
|
|
|
|
|
|
|
file := af.file |
|
|
file := af.file |
|
|
if file == nil { |
|
|
if file == nil { |
|
|
return nil |
|
|
return nil |
|
@ -201,6 +209,56 @@ func (af *AutoFile) openFile() error { |
|
|
return nil |
|
|
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 { |
|
@ -209,6 +267,8 @@ func Tempfile(prefix string) (*os.File, string) { |
|
|
return file, file.Name() |
|
|
return file, file.Name() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
func Prompt(prompt string, defaultValue string) (string, error) { |
|
|
func Prompt(prompt string, defaultValue string) (string, error) { |
|
|
fmt.Print(prompt) |
|
|
fmt.Print(prompt) |
|
|
reader := bufio.NewReader(os.Stdin) |
|
|
reader := bufio.NewReader(os.Stdin) |
|
|