@ -11,6 +11,7 @@ import (
"os"
"os"
"os/signal"
"os/signal"
"runtime/pprof"
"runtime/pprof"
"strconv"
"sync"
"sync"
"syscall"
"syscall"
"time"
"time"
@ -45,7 +46,7 @@ type Manager struct {
func newGracefulManager ( ctx context . Context ) * Manager {
func newGracefulManager ( ctx context . Context ) * Manager {
manager := & Manager {
manager := & Manager {
isChild : len ( os . Getenv ( listenFDs ) ) > 0 && os . Getppid ( ) > 1 ,
isChild : len ( os . Getenv ( listenFDsEnv ) ) > 0 && os . Getppid ( ) > 1 ,
lock : & sync . RWMutex { } ,
lock : & sync . RWMutex { } ,
}
}
manager . createServerWaitGroup . Add ( numberOfServersToCreate )
manager . createServerWaitGroup . Add ( numberOfServersToCreate )
@ -53,6 +54,41 @@ func newGracefulManager(ctx context.Context) *Manager {
return manager
return manager
}
}
type systemdNotifyMsg string
const (
readyMsg systemdNotifyMsg = "READY=1"
stoppingMsg systemdNotifyMsg = "STOPPING=1"
reloadingMsg systemdNotifyMsg = "RELOADING=1"
watchdogMsg systemdNotifyMsg = "WATCHDOG=1"
)
func statusMsg ( msg string ) systemdNotifyMsg {
return systemdNotifyMsg ( "STATUS=" + msg )
}
func pidMsg ( ) systemdNotifyMsg {
return systemdNotifyMsg ( "MAINPID=" + strconv . Itoa ( os . Getpid ( ) ) )
}
// Notify systemd of status via the notify protocol
func ( g * Manager ) notify ( msg systemdNotifyMsg ) {
conn , err := getNotifySocket ( )
if err != nil {
// the err is logged in getNotifySocket
return
}
if conn == nil {
return
}
defer conn . Close ( )
if _ , err = conn . Write ( [ ] byte ( msg ) ) ; err != nil {
log . Warn ( "Failed to notify NOTIFY_SOCKET: %v" , err )
return
}
}
func ( g * Manager ) start ( ctx context . Context ) {
func ( g * Manager ) start ( ctx context . Context ) {
// Make contexts
// Make contexts
g . terminateCtx , g . terminateCtxCancel = context . WithCancel ( ctx )
g . terminateCtx , g . terminateCtxCancel = context . WithCancel ( ctx )
@ -72,6 +108,8 @@ func (g *Manager) start(ctx context.Context) {
// Set the running state & handle signals
// Set the running state & handle signals
g . setState ( stateRunning )
g . setState ( stateRunning )
g . notify ( statusMsg ( "Starting Gitea" ) )
g . notify ( pidMsg ( ) )
go g . handleSignals ( g . managerCtx )
go g . handleSignals ( g . managerCtx )
// Handle clean up of unused provided listeners and delayed start-up
// Handle clean up of unused provided listeners and delayed start-up
@ -84,6 +122,7 @@ func (g *Manager) start(ctx context.Context) {
// Ignore the error here there's not much we can do with it
// Ignore the error here there's not much we can do with it
// They're logged in the CloseProvidedListeners function
// They're logged in the CloseProvidedListeners function
_ = CloseProvidedListeners ( )
_ = CloseProvidedListeners ( )
g . notify ( readyMsg )
} ( )
} ( )
if setting . StartupTimeout > 0 {
if setting . StartupTimeout > 0 {
go func ( ) {
go func ( ) {
@ -104,6 +143,8 @@ func (g *Manager) start(ctx context.Context) {
return
return
case <- time . After ( setting . StartupTimeout ) :
case <- time . After ( setting . StartupTimeout ) :
log . Error ( "Startup took too long! Shutting down" )
log . Error ( "Startup took too long! Shutting down" )
g . notify ( statusMsg ( "Startup took too long! Shutting down" ) )
g . notify ( stoppingMsg )
g . doShutdown ( )
g . doShutdown ( )
}
}
} ( )
} ( )
@ -126,6 +167,13 @@ func (g *Manager) handleSignals(ctx context.Context) {
syscall . SIGTSTP ,
syscall . SIGTSTP ,
)
)
watchdogTimeout := getWatchdogTimeout ( )
t := & time . Ticker { }
if watchdogTimeout != 0 {
g . notify ( watchdogMsg )
t = time . NewTicker ( watchdogTimeout / 2 )
}
pid := syscall . Getpid ( )
pid := syscall . Getpid ( )
for {
for {
select {
select {
@ -136,6 +184,7 @@ func (g *Manager) handleSignals(ctx context.Context) {
g . DoGracefulRestart ( )
g . DoGracefulRestart ( )
case syscall . SIGUSR1 :
case syscall . SIGUSR1 :
log . Warn ( "PID %d. Received SIGUSR1. Releasing and reopening logs" , pid )
log . Warn ( "PID %d. Received SIGUSR1. Releasing and reopening logs" , pid )
g . notify ( statusMsg ( "Releasing and reopening logs" ) )
if err := log . ReleaseReopen ( ) ; err != nil {
if err := log . ReleaseReopen ( ) ; err != nil {
log . Error ( "Error whilst releasing and reopening logs: %v" , err )
log . Error ( "Error whilst releasing and reopening logs: %v" , err )
}
}
@ -153,6 +202,8 @@ func (g *Manager) handleSignals(ctx context.Context) {
default :
default :
log . Info ( "PID %d. Received %v." , pid , sig )
log . Info ( "PID %d. Received %v." , pid , sig )
}
}
case <- t . C :
g . notify ( watchdogMsg )
case <- ctx . Done ( ) :
case <- ctx . Done ( ) :
log . Warn ( "PID: %d. Background context for manager closed - %v - Shutting down..." , pid , ctx . Err ( ) )
log . Warn ( "PID: %d. Background context for manager closed - %v - Shutting down..." , pid , ctx . Err ( ) )
g . DoGracefulShutdown ( )
g . DoGracefulShutdown ( )
@ -169,6 +220,9 @@ func (g *Manager) doFork() error {
}
}
g . forked = true
g . forked = true
g . lock . Unlock ( )
g . lock . Unlock ( )
g . notify ( reloadingMsg )
// We need to move the file logs to append pids
// We need to move the file logs to append pids
setting . RestartLogsWithPIDSuffix ( )
setting . RestartLogsWithPIDSuffix ( )
@ -191,18 +245,27 @@ func (g *Manager) DoGracefulRestart() {
}
}
} else {
} else {
log . Info ( "PID: %d. Not set restartable. Shutting down..." , os . Getpid ( ) )
log . Info ( "PID: %d. Not set restartable. Shutting down..." , os . Getpid ( ) )
g . notify ( stoppingMsg )
g . doShutdown ( )
g . doShutdown ( )
}
}
}
}
// DoImmediateHammer causes an immediate hammer
// DoImmediateHammer causes an immediate hammer
func ( g * Manager ) DoImmediateHammer ( ) {
func ( g * Manager ) DoImmediateHammer ( ) {
g . notify ( statusMsg ( "Sending immediate hammer" ) )
g . doHammerTime ( 0 * time . Second )
g . doHammerTime ( 0 * time . Second )
}
}
// DoGracefulShutdown causes a graceful shutdown
// DoGracefulShutdown causes a graceful shutdown
func ( g * Manager ) DoGracefulShutdown ( ) {
func ( g * Manager ) DoGracefulShutdown ( ) {
g . lock . Lock ( )
if ! g . forked {
g . lock . Unlock ( )
g . notify ( stoppingMsg )
} else {
g . lock . Unlock ( )
g . notify ( statusMsg ( "Shutting down after fork" ) )
}
g . doShutdown ( )
g . doShutdown ( )
}
}