mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-25 23:02:16 +00:00
libs: Handle SIGHUP explicitly inside autofile (#2480)
* handle SIGHUP explicitly inside autofile Refs #2260 * libs: Use consistent channel suffix
This commit is contained in:
parent
d12e55c494
commit
eb0da7f9cb
@ -2,7 +2,9 @@ package autofile
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
@ -32,50 +34,70 @@ if err != nil {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const (
|
const (
|
||||||
autoFileOpenDuration = 1000 * time.Millisecond
|
autoFileClosePeriod = 1000 * time.Millisecond
|
||||||
autoFilePerms = os.FileMode(0600)
|
autoFilePerms = os.FileMode(0600)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Automatically closes and re-opens file for writing.
|
// AutoFile automatically closes and re-opens file for writing. The file is
|
||||||
|
// automatically setup to close itself every 1s and upon receiving SIGHUP.
|
||||||
|
//
|
||||||
// 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
|
ID string
|
||||||
Path string
|
Path string
|
||||||
ticker *time.Ticker
|
|
||||||
tickerStopped chan struct{} // closed when ticker is stopped
|
closeTicker *time.Ticker
|
||||||
|
closeTickerStopc chan struct{} // closed when closeTicker is stopped
|
||||||
|
hupc chan os.Signal
|
||||||
|
|
||||||
mtx sync.Mutex
|
mtx sync.Mutex
|
||||||
file *os.File
|
file *os.File
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenAutoFile(path string) (af *AutoFile, err error) {
|
// OpenAutoFile creates an AutoFile in the path (with random ID). If there is
|
||||||
af = &AutoFile{
|
// an error, it will be of type *PathError or *ErrPermissionsChanged (if file's
|
||||||
|
// permissions got changed (should be 0600)).
|
||||||
|
func OpenAutoFile(path string) (*AutoFile, error) {
|
||||||
|
af := &AutoFile{
|
||||||
ID: cmn.RandStr(12) + ":" + path,
|
ID: cmn.RandStr(12) + ":" + path,
|
||||||
Path: path,
|
Path: path,
|
||||||
ticker: time.NewTicker(autoFileOpenDuration),
|
closeTicker: time.NewTicker(autoFileClosePeriod),
|
||||||
tickerStopped: make(chan struct{}),
|
closeTickerStopc: make(chan struct{}),
|
||||||
}
|
}
|
||||||
if err = af.openFile(); err != nil {
|
if err := af.openFile(); err != nil {
|
||||||
return
|
af.Close()
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
go af.processTicks()
|
|
||||||
sighupWatchers.addAutoFile(af)
|
// Close file on SIGHUP.
|
||||||
return
|
af.hupc = make(chan os.Signal, 1)
|
||||||
|
signal.Notify(af.hupc, syscall.SIGHUP)
|
||||||
|
go func() {
|
||||||
|
for range af.hupc {
|
||||||
|
af.closeFile()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go af.closeFileRoutine()
|
||||||
|
|
||||||
|
return af, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (af *AutoFile) Close() error {
|
func (af *AutoFile) Close() error {
|
||||||
af.ticker.Stop()
|
af.closeTicker.Stop()
|
||||||
close(af.tickerStopped)
|
close(af.closeTickerStopc)
|
||||||
err := af.closeFile()
|
if af.hupc != nil {
|
||||||
sighupWatchers.removeAutoFile(af)
|
close(af.hupc)
|
||||||
return err
|
}
|
||||||
|
return af.closeFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (af *AutoFile) processTicks() {
|
func (af *AutoFile) closeFileRoutine() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-af.ticker.C:
|
case <-af.closeTicker.C:
|
||||||
af.closeFile()
|
af.closeFile()
|
||||||
case <-af.tickerStopped:
|
case <-af.closeTickerStopc:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,6 +111,7 @@ func (af *AutoFile) closeFile() (err error) {
|
|||||||
if file == nil {
|
if file == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
af.file = nil
|
af.file = nil
|
||||||
return file.Close()
|
return file.Close()
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package autofile
|
|||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"sync/atomic"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -37,13 +36,10 @@ func TestSIGHUP(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Send SIGHUP to self.
|
// Send SIGHUP to self.
|
||||||
oldSighupCounter := atomic.LoadInt32(&sighupCounter)
|
|
||||||
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
|
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
|
||||||
|
|
||||||
// Wait a bit... signals are not handled synchronously.
|
// Wait a bit... signals are not handled synchronously.
|
||||||
for atomic.LoadInt32(&sighupCounter) == oldSighupCounter {
|
|
||||||
time.Sleep(time.Millisecond * 10)
|
time.Sleep(time.Millisecond * 10)
|
||||||
}
|
|
||||||
|
|
||||||
// Write more to the file.
|
// Write more to the file.
|
||||||
_, err = af.Write([]byte("Line 3\n"))
|
_, err = af.Write([]byte("Line 3\n"))
|
||||||
@ -87,7 +83,4 @@ func TestOpenAutoFilePerms(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
t.Errorf("unexpected error %v", e)
|
t.Errorf("unexpected error %v", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = af.Close()
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
}
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
package autofile
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
initSighupWatcher()
|
|
||||||
}
|
|
||||||
|
|
||||||
var sighupWatchers *SighupWatcher
|
|
||||||
var sighupCounter int32 // For testing
|
|
||||||
|
|
||||||
func initSighupWatcher() {
|
|
||||||
sighupWatchers = newSighupWatcher()
|
|
||||||
|
|
||||||
hup := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(hup, syscall.SIGHUP)
|
|
||||||
|
|
||||||
quit := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-hup:
|
|
||||||
sighupWatchers.closeAll()
|
|
||||||
atomic.AddInt32(&sighupCounter, 1)
|
|
||||||
case <-quit:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watchces for SIGHUP events and notifies registered AutoFiles
|
|
||||||
type SighupWatcher struct {
|
|
||||||
mtx sync.Mutex
|
|
||||||
autoFiles map[string]*AutoFile
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSighupWatcher() *SighupWatcher {
|
|
||||||
return &SighupWatcher{
|
|
||||||
autoFiles: make(map[string]*AutoFile, 10),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *SighupWatcher) addAutoFile(af *AutoFile) {
|
|
||||||
w.mtx.Lock()
|
|
||||||
w.autoFiles[af.ID] = af
|
|
||||||
w.mtx.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// If AutoFile isn't registered or was already removed, does nothing.
|
|
||||||
func (w *SighupWatcher) removeAutoFile(af *AutoFile) {
|
|
||||||
w.mtx.Lock()
|
|
||||||
delete(w.autoFiles, af.ID)
|
|
||||||
w.mtx.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *SighupWatcher) closeAll() {
|
|
||||||
w.mtx.Lock()
|
|
||||||
for _, af := range w.autoFiles {
|
|
||||||
af.closeFile()
|
|
||||||
}
|
|
||||||
w.mtx.Unlock()
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user