Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
141 lines
3.3 KiB
141 lines
3.3 KiB
// +build !windows |
|
|
|
// Copyright 2019 The Gitea Authors. All rights reserved. |
|
// Use of this source code is governed by a MIT-style |
|
// license that can be found in the LICENSE file. |
|
|
|
package graceful |
|
|
|
import ( |
|
"errors" |
|
"os" |
|
"os/signal" |
|
"sync" |
|
"syscall" |
|
"time" |
|
|
|
"code.gitea.io/gitea/modules/log" |
|
"code.gitea.io/gitea/modules/setting" |
|
) |
|
|
|
type gracefulManager struct { |
|
isChild bool |
|
forked bool |
|
lock *sync.RWMutex |
|
state state |
|
shutdown chan struct{} |
|
hammer chan struct{} |
|
terminate chan struct{} |
|
runningServerWaitGroup sync.WaitGroup |
|
createServerWaitGroup sync.WaitGroup |
|
terminateWaitGroup sync.WaitGroup |
|
} |
|
|
|
func newGracefulManager() *gracefulManager { |
|
manager := &gracefulManager{ |
|
isChild: len(os.Getenv(listenFDs)) > 0 && os.Getppid() > 1, |
|
lock: &sync.RWMutex{}, |
|
} |
|
manager.createServerWaitGroup.Add(numberOfServersToCreate) |
|
manager.Run() |
|
return manager |
|
} |
|
|
|
func (g *gracefulManager) Run() { |
|
g.setState(stateRunning) |
|
go g.handleSignals() |
|
c := make(chan struct{}) |
|
go func() { |
|
defer close(c) |
|
// Wait till we're done getting all of the listeners and then close |
|
// the unused ones |
|
g.createServerWaitGroup.Wait() |
|
// Ignore the error here there's not much we can do with it |
|
// They're logged in the CloseProvidedListeners function |
|
_ = CloseProvidedListeners() |
|
}() |
|
if setting.StartupTimeout > 0 { |
|
go func() { |
|
select { |
|
case <-c: |
|
return |
|
case <-g.IsShutdown(): |
|
return |
|
case <-time.After(setting.StartupTimeout): |
|
log.Error("Startup took too long! Shutting down") |
|
g.doShutdown() |
|
} |
|
}() |
|
} |
|
} |
|
|
|
func (g *gracefulManager) handleSignals() { |
|
var sig os.Signal |
|
|
|
signalChannel := make(chan os.Signal, 1) |
|
|
|
signal.Notify( |
|
signalChannel, |
|
syscall.SIGHUP, |
|
syscall.SIGUSR1, |
|
syscall.SIGUSR2, |
|
syscall.SIGINT, |
|
syscall.SIGTERM, |
|
syscall.SIGTSTP, |
|
) |
|
|
|
pid := syscall.Getpid() |
|
for { |
|
sig = <-signalChannel |
|
switch sig { |
|
case syscall.SIGHUP: |
|
if setting.GracefulRestartable { |
|
log.Info("PID: %d. Received SIGHUP. Forking...", pid) |
|
err := g.doFork() |
|
if err != nil && err.Error() != "another process already forked. Ignoring this one" { |
|
log.Error("Error whilst forking from PID: %d : %v", pid, err) |
|
} |
|
} else { |
|
log.Info("PID: %d. Received SIGHUP. Not set restartable. Shutting down...", pid) |
|
|
|
g.doShutdown() |
|
} |
|
case syscall.SIGUSR1: |
|
log.Info("PID %d. Received SIGUSR1.", pid) |
|
case syscall.SIGUSR2: |
|
log.Warn("PID %d. Received SIGUSR2. Hammering...", pid) |
|
g.doHammerTime(0 * time.Second) |
|
case syscall.SIGINT: |
|
log.Warn("PID %d. Received SIGINT. Shutting down...", pid) |
|
g.doShutdown() |
|
case syscall.SIGTERM: |
|
log.Warn("PID %d. Received SIGTERM. Shutting down...", pid) |
|
g.doShutdown() |
|
case syscall.SIGTSTP: |
|
log.Info("PID %d. Received SIGTSTP.", pid) |
|
default: |
|
log.Info("PID %d. Received %v.", pid, sig) |
|
} |
|
} |
|
} |
|
|
|
func (g *gracefulManager) doFork() error { |
|
g.lock.Lock() |
|
if g.forked { |
|
g.lock.Unlock() |
|
return errors.New("another process already forked. Ignoring this one") |
|
} |
|
g.forked = true |
|
g.lock.Unlock() |
|
// We need to move the file logs to append pids |
|
setting.RestartLogsWithPIDSuffix() |
|
|
|
_, err := RestartProcess() |
|
|
|
return err |
|
} |
|
|
|
func (g *gracefulManager) RegisterServer() { |
|
KillParent() |
|
g.runningServerWaitGroup.Add(1) |
|
}
|
|
|