Add Graceful shutdown for Windows and hooks for shutdown of goroutines (#8964)
* Graceful Shutdown for windows and others Restructures modules/graceful, adding shutdown for windows, removing and replacing the old minwinsvc code. Creates a new waitGroup - terminate which allows for goroutines to finish up after the shutdown of the servers. Shutdown and terminate hooks are added for goroutines. * Remove unused functions - these can be added in a different PR * Add startup timeout functionality * Document STARTUP_TIMEOUTlunny/display_deleted_branch2
parent
d7ac9727bb
commit
cbaa1de9ec
@ -1,37 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
// Copyright 2016 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 cmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func runHTTP(listenAddr string, m http.Handler) error {
|
||||
return http.ListenAndServe(listenAddr, m)
|
||||
}
|
||||
|
||||
func runHTTPS(listenAddr, certFile, keyFile string, m http.Handler) error {
|
||||
return http.ListenAndServeTLS(listenAddr, certFile, keyFile, m)
|
||||
}
|
||||
|
||||
func runHTTPSWithTLSConfig(listenAddr string, tlsConfig *tls.Config, m http.Handler) error {
|
||||
server := &http.Server{
|
||||
Addr: listenAddr,
|
||||
Handler: m,
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
return server.ListenAndServeTLS("", "")
|
||||
}
|
||||
|
||||
// NoHTTPRedirector is a no-op on Windows
|
||||
func NoHTTPRedirector() {
|
||||
}
|
||||
|
||||
// NoMainListener is a no-op on Windows
|
||||
func NoMainListener() {
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
// +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 "sync"
|
||||
|
||||
var cleanupWaitGroup sync.WaitGroup
|
||||
|
||||
func init() {
|
||||
cleanupWaitGroup = sync.WaitGroup{}
|
||||
|
||||
// There are three places that could inherit sockets:
|
||||
//
|
||||
// * HTTP or HTTPS main listener
|
||||
// * HTTP redirection fallback
|
||||
// * SSH
|
||||
//
|
||||
// If you add an additional place you must increment this number
|
||||
// and add a function to call InformCleanup if it's not going to be used
|
||||
cleanupWaitGroup.Add(3)
|
||||
|
||||
// Wait till we're done getting all of the listeners and then close
|
||||
// the unused ones
|
||||
go func() {
|
||||
cleanupWaitGroup.Wait()
|
||||
// Ignore the error here there's not much we can do with it
|
||||
// They're logged in the CloseProvidedListeners function
|
||||
_ = CloseProvidedListeners()
|
||||
}()
|
||||
}
|
||||
|
||||
// InformCleanup tells the cleanup wait group that we have either taken a listener
|
||||
// or will not be taking a listener
|
||||
func InformCleanup() {
|
||||
cleanupWaitGroup.Done()
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
// +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.
|
||||
// This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
|
||||
|
||||
package graceful
|
||||
|
||||
// This file contains shims for windows builds
|
||||
const IsChild = false
|
||||
|
||||
// WaitForServers waits for all running servers to finish
|
||||
func WaitForServers() {
|
||||
|
||||
}
|
@ -0,0 +1,187 @@
|
||||
// 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 (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
type state uint8
|
||||
|
||||
const (
|
||||
stateInit state = iota
|
||||
stateRunning
|
||||
stateShuttingDown
|
||||
stateTerminate
|
||||
)
|
||||
|
||||
// There are three places that could inherit sockets:
|
||||
//
|
||||
// * HTTP or HTTPS main listener
|
||||
// * HTTP redirection fallback
|
||||
// * SSH
|
||||
//
|
||||
// If you add an additional place you must increment this number
|
||||
// and add a function to call manager.InformCleanup if it's not going to be used
|
||||
const numberOfServersToCreate = 3
|
||||
|
||||
// Manager represents the graceful server manager interface
|
||||
var Manager *gracefulManager
|
||||
|
||||
func init() {
|
||||
Manager = newGracefulManager()
|
||||
}
|
||||
|
||||
func (g *gracefulManager) doShutdown() {
|
||||
if !g.setStateTransition(stateRunning, stateShuttingDown) {
|
||||
return
|
||||
}
|
||||
g.lock.Lock()
|
||||
close(g.shutdown)
|
||||
g.lock.Unlock()
|
||||
|
||||
if setting.GracefulHammerTime >= 0 {
|
||||
go g.doHammerTime(setting.GracefulHammerTime)
|
||||
}
|
||||
go func() {
|
||||
g.WaitForServers()
|
||||
<-time.After(1 * time.Second)
|
||||
g.doTerminate()
|
||||
}()
|
||||
}
|
||||
|
||||
func (g *gracefulManager) doHammerTime(d time.Duration) {
|
||||
time.Sleep(d)
|
||||
select {
|
||||
case <-g.hammer:
|
||||
default:
|
||||
log.Warn("Setting Hammer condition")
|
||||
close(g.hammer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (g *gracefulManager) doTerminate() {
|
||||
if !g.setStateTransition(stateShuttingDown, stateTerminate) {
|
||||
return
|
||||
}
|
||||
g.lock.Lock()
|
||||
close(g.terminate)
|
||||
g.lock.Unlock()
|
||||
}
|
||||
|
||||
// IsChild returns if the current process is a child of previous Gitea process
|
||||
func (g *gracefulManager) IsChild() bool {
|
||||
return g.isChild
|
||||
}
|
||||
|
||||
// IsShutdown returns a channel which will be closed at shutdown.
|
||||
// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate
|
||||
func (g *gracefulManager) IsShutdown() <-chan struct{} {
|
||||
g.lock.RLock()
|
||||
if g.shutdown == nil {
|
||||
g.lock.RUnlock()
|
||||
g.lock.Lock()
|
||||
if g.shutdown == nil {
|
||||
g.shutdown = make(chan struct{})
|
||||
}
|
||||
defer g.lock.Unlock()
|
||||
return g.shutdown
|
||||
}
|
||||
defer g.lock.RUnlock()
|
||||
return g.shutdown
|
||||
}
|
||||
|
||||
// IsHammer returns a channel which will be closed at hammer
|
||||
// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate
|
||||
// Servers running within the running server wait group should respond to IsHammer
|
||||
// if not shutdown already
|
||||
func (g *gracefulManager) IsHammer() <-chan struct{} {
|
||||
g.lock.RLock()
|
||||
if g.hammer == nil {
|
||||
g.lock.RUnlock()
|
||||
g.lock.Lock()
|
||||
if g.hammer == nil {
|
||||
g.hammer = make(chan struct{})
|
||||
}
|
||||
defer g.lock.Unlock()
|
||||
return g.hammer
|
||||
}
|
||||
defer g.lock.RUnlock()
|
||||
return g.hammer
|
||||
}
|
||||
|
||||
// IsTerminate returns a channel which will be closed at terminate
|
||||
// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate
|
||||
// IsTerminate will only close once all running servers have stopped
|
||||
func (g *gracefulManager) IsTerminate() <-chan struct{} {
|
||||
g.lock.RLock()
|
||||
if g.terminate == nil {
|
||||
g.lock.RUnlock()
|
||||
g.lock.Lock()
|
||||
if g.terminate == nil {
|
||||
g.terminate = make(chan struct{})
|
||||
}
|
||||
defer g.lock.Unlock()
|
||||
return g.terminate
|
||||
}
|
||||
defer g.lock.RUnlock()
|
||||
return g.terminate
|
||||
}
|
||||
|
||||
// ServerDone declares a running server done and subtracts one from the
|
||||
// running server wait group. Users probably do not want to call this
|
||||
// and should use one of the RunWithShutdown* functions
|
||||
func (g *gracefulManager) ServerDone() {
|
||||
g.runningServerWaitGroup.Done()
|
||||
}
|
||||
|
||||
// WaitForServers waits for all running servers to finish. Users should probably
|
||||
// instead use AtTerminate or IsTerminate
|
||||
func (g *gracefulManager) WaitForServers() {
|
||||
g.runningServerWaitGroup.Wait()
|
||||
}
|
||||
|
||||
// WaitForTerminate waits for all terminating actions to finish.
|
||||
// Only the main go-routine should use this
|
||||
func (g *gracefulManager) WaitForTerminate() {
|
||||
g.terminateWaitGroup.Wait()
|
||||
}
|
||||
|
||||
func (g *gracefulManager) getState() state {
|
||||
g.lock.RLock()
|
||||
defer g.lock.RUnlock()
|
||||
return g.state
|
||||
}
|
||||
|
||||
func (g *gracefulManager) setStateTransition(old, new state) bool {
|
||||
if old != g.getState() {
|
||||
return false
|
||||
}
|
||||
g.lock.Lock()
|
||||
if g.state != old {
|
||||
g.lock.Unlock()
|
||||
return false
|
||||
}
|
||||
g.state = new
|
||||
g.lock.Unlock()
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *gracefulManager) setState(st state) {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
|
||||
g.state = st
|
||||
}
|
||||
|
||||
// InformCleanup tells the cleanup wait group that we have either taken a listener
|
||||
// or will not be taking a listener
|
||||
func (g *gracefulManager) InformCleanup() {
|
||||
g.createServerWaitGroup.Done()
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
// +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)
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
// +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.
|
||||
// This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
|
||||
|
||||
package graceful
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/debug"
|
||||
)
|
||||
|
||||
var WindowsServiceName = "gitea"
|
||||
|
||||
const (
|
||||
hammerCode = 128
|
||||
hammerCmd = svc.Cmd(hammerCode)
|
||||
acceptHammerCode = svc.Accepted(hammerCode)
|
||||
)
|
||||
|
||||
|
||||
type gracefulManager struct {
|
||||
isChild 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: false,
|
||||
lock: &sync.RWMutex{},
|
||||
}
|
||||
manager.createServerWaitGroup.Add(numberOfServersToCreate)
|
||||
manager.Run()
|
||||
return manager
|
||||
}
|
||||
|
||||
func (g *gracefulManager) Run() {
|
||||
g.setState(stateRunning)
|
||||
if skip, _ := strconv.ParseBool(os.Getenv("SKIP_MINWINSVC")); skip {
|
||||
return
|
||||
}
|
||||
run := svc.Run
|
||||
isInteractive, err := svc.IsAnInteractiveSession()
|
||||
if err != nil {
|
||||
log.Error("Unable to ascertain if running as an Interactive Session: %v", err)
|
||||
return
|
||||
}
|
||||
if isInteractive {
|
||||
run = debug.Run
|
||||
}
|
||||
go run(WindowsServiceName, g)
|
||||
}
|
||||
|
||||
// Execute makes gracefulManager implement svc.Handler
|
||||
func (g *gracefulManager) Execute(args []string, changes <-chan svc.ChangeRequest, status chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
|
||||
if setting.StartupTimeout > 0 {
|
||||
status <- svc.Status{State: svc.StartPending}
|
||||
} else {
|
||||
status <- svc.Status{State: svc.StartPending, WaitHint: uint32(setting.StartupTimeout/time.Millisecond)}
|
||||
}
|
||||
|
||||
// Now need to wait for everything to start...
|
||||
if !g.awaitServer(setting.StartupTimeout) {
|
||||
return false, 1
|
||||
}
|
||||
|
||||
// We need to implement some way of svc.AcceptParamChange/svc.ParamChange
|
||||
status <- svc.Status{
|
||||
State: svc.Running,
|
||||
Accepts: svc.AcceptStop | svc.AcceptShutdown | acceptHammerCode,
|
||||
}
|
||||
|
||||
waitTime := 30 * time.Second
|
||||
|
||||
loop:
|
||||
for change := range changes {
|
||||
switch change.Cmd {
|
||||
case svc.Interrogate:
|
||||
status <- change.CurrentStatus
|
||||
case svc.Stop, svc.Shutdown:
|
||||
g.doShutdown()
|
||||
waitTime += setting.GracefulHammerTime
|
||||
break loop
|
||||
case hammerCode:
|
||||
g.doShutdown()
|
||||
g.doHammerTime(0 *time.Second)
|
||||
break loop
|
||||
default:
|
||||
log.Debug("Unexpected control request: %v", change.Cmd)
|
||||
}
|
||||
}
|
||||
|
||||
status <- svc.Status{
|
||||
State: svc.StopPending,
|
||||
WaitHint: uint32(waitTime/time.Millisecond),
|
||||
}
|
||||
|
||||
hammerLoop:
|
||||
for {
|
||||
select {
|
||||
case change := <-changes:
|
||||
switch change.Cmd {
|
||||
case svc.Interrogate:
|
||||
status <- change.CurrentStatus
|
||||
case svc.Stop, svc.Shutdown, hammerCmd:
|
||||
g.doHammerTime(0 * time.Second)
|
||||
break hammerLoop
|
||||
default:
|
||||
log.Debug("Unexpected control request: %v", change.Cmd)
|
||||
}
|
||||
case <-g.hammer:
|
||||
break hammerLoop
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
|
||||
func (g *gracefulManager) RegisterServer() {
|
||||
g.runningServerWaitGroup.Add(1)
|
||||
}
|
||||
|
||||
func (g *gracefulManager) awaitServer(limit time.Duration) bool {
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
g.createServerWaitGroup.Wait()
|
||||
}()
|
||||
if limit > 0 {
|
||||
select {
|
||||
case <-c:
|
||||
return true // completed normally
|
||||
case <-time.After(limit):
|
||||
return false // timed out
|
||||
case <-g.IsShutdown():
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case <-c:
|
||||
return true // completed normally
|
||||
case <-g.IsShutdown():
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
// +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.
|
||||
// This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
|
||||
|
||||
package graceful
|
||||
|
||||
import "net"
|
||||
|
||||
// GetListener obtains a listener for the local network address.
|
||||
// On windows this is basically just a shim around net.Listen.
|
||||
func GetListener(network, address string) (net.Listener, error) {
|
||||
// Add a deferral to say that we've tried to grab a listener
|
||||
defer Manager.InformCleanup()
|
||||
|
||||
return net.Listen(network, address)
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
// +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 (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
var hookableSignals []os.Signal
|
||||
|
||||
func init() {
|
||||
hookableSignals = []os.Signal{
|
||||
syscall.SIGHUP,
|
||||
syscall.SIGUSR1,
|
||||
syscall.SIGUSR2,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGTSTP,
|
||||
}
|
||||
}
|
||||
|
||||
// handleSignals listens for os Signals and calls any hooked in function that the
|
||||
// user had registered with the signal.
|
||||
func (srv *Server) handleSignals() {
|
||||
var sig os.Signal
|
||||
|
||||
signal.Notify(
|
||||
srv.sigChan,
|
||||
hookableSignals...,
|
||||
)
|
||||
|
||||
pid := syscall.Getpid()
|
||||
for {
|
||||
sig = <-srv.sigChan
|
||||
srv.preSignalHooks(sig)
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
if setting.GracefulRestartable {
|
||||
log.Info("PID: %d. Received SIGHUP. Forking...", pid)
|
||||
err := srv.fork()
|
||||
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)
|
||||
|
||||
srv.shutdown()
|
||||
}
|
||||
case syscall.SIGUSR1:
|
||||
log.Info("PID %d. Received SIGUSR1.", pid)
|
||||
case syscall.SIGUSR2:
|
||||
log.Warn("PID %d. Received SIGUSR2. Hammering...", pid)
|
||||
srv.hammerTime(0 * time.Second)
|
||||
case syscall.SIGINT:
|
||||
log.Warn("PID %d. Received SIGINT. Shutting down...", pid)
|
||||
srv.shutdown()
|
||||
case syscall.SIGTERM:
|
||||
log.Warn("PID %d. Received SIGTERM. Shutting down...", pid)
|
||||
srv.shutdown()
|
||||
case syscall.SIGTSTP:
|
||||
log.Info("PID %d. Received SIGTSTP.")
|
||||
default:
|
||||
log.Info("PID %d. Received %v.", sig)
|
||||
}
|
||||
srv.postSignalHooks(sig)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) preSignalHooks(sig os.Signal) {
|
||||
if _, notSet := srv.PreSignalHooks[sig]; !notSet {
|
||||
return
|
||||
}
|
||||
for _, f := range srv.PreSignalHooks[sig] {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) postSignalHooks(sig os.Signal) {
|
||||
if _, notSet := srv.PostSignalHooks[sig]; !notSet {
|
||||
return
|
||||
}
|
||||
for _, f := range srv.PostSignalHooks[sig] {
|
||||
f()
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
Copyright (c) 2015 Daniel Theophanes
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
@ -1,18 +0,0 @@
|
||||
### Minimal windows service stub
|
||||
|
||||
Programs designed to run from most *nix style operating systems
|
||||
can import this package to enable running programs as services without modifying
|
||||
them.
|
||||
|
||||
```
|
||||
import _ "github.com/kardianos/minwinsvc"
|
||||
```
|
||||
|
||||
If you need more control over the exit behavior, set
|
||||
```
|
||||
minwinsvc.SetOnExit(func() {
|
||||
// Do something.
|
||||
// Within 10 seconds call:
|
||||
os.Exit(0)
|
||||
})
|
||||
```
|
@ -1,18 +0,0 @@
|
||||
// Copyright 2015 Daniel Theophanes.
|
||||
// Use of this source code is governed by a zlib-style
|
||||
// license that can be found in the LICENSE file.package service
|
||||
|
||||
// Package minwinsvc is a minimal non-invasive windows only service stub.
|
||||
//
|
||||
// Import to allow running as a windows service.
|
||||
// import _ "github.com/kardianos/minwinsvc"
|
||||
// This will detect if running as a windows service
|
||||
// and install required callbacks for windows.
|
||||
package minwinsvc
|
||||
|
||||
// SetOnExit sets the function to be called when the windows service
|
||||
// requests an exit. If this is not called, or if it is called where
|
||||
// f == nil, then it defaults to calling "os.Exit(0)".
|
||||
func SetOnExit(f func()) {
|
||||
setOnExit(f)
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
// Copyright 2015 Daniel Theophanes.
|
||||
// Use of this source code is governed by a zlib-style
|
||||
// license that can be found in the LICENSE file.package service
|
||||
|
||||
//+build !windows
|
||||
|
||||
package minwinsvc
|
||||
|
||||
func setOnExit(f func()) {
|
||||
// Nothing.
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
// Copyright 2015 Daniel Theophanes.
|
||||
// Use of this source code is governed by a zlib-style
|
||||
// license that can be found in the LICENSE file.package service
|
||||
|
||||
//+build windows
|
||||
|
||||
package minwinsvc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sys/windows/svc"
|
||||
)
|
||||
|
||||
var (
|
||||
onExit func()
|
||||
guard sync.Mutex
|
||||
skip, _ = strconv.ParseBool(os.Getenv("SKIP_MINWINSVC"))
|
||||
isSSH = os.Getenv("SSH_ORIGINAL_COMMAND") != ""
|
||||
)
|
||||
|
||||
func init() {
|
||||
if skip || isSSH {
|
||||
return
|
||||
}
|
||||
interactive, err := svc.IsAnInteractiveSession()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if interactive {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
_ = svc.Run("", runner{})
|
||||
|
||||
guard.Lock()
|
||||
f := onExit
|
||||
guard.Unlock()
|
||||
|
||||
// Don't hold this lock in user code.
|
||||
if f != nil {
|
||||
f()
|
||||
}
|
||||
// Make sure we exit.
|
||||
os.Exit(0)
|
||||
}()
|
||||
}
|
||||
|
||||
func setOnExit(f func()) {
|
||||
guard.Lock()
|
||||
onExit = f
|
||||
guard.Unlock()
|
||||
}
|
||||
|
||||
type runner struct{}
|
||||
|
||||
func (runner) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (bool, uint32) {
|
||||
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
|
||||
changes <- svc.Status{State: svc.StartPending}
|
||||
|
||||
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
||||
for {
|
||||
c := <-r
|
||||
switch c.Cmd {
|
||||
case svc.Interrogate:
|
||||
changes <- c.CurrentStatus
|
||||
case svc.Stop, svc.Shutdown:
|
||||
changes <- svc.Status{State: svc.StopPending}
|
||||
return false, 0
|
||||
}
|
||||
}
|
||||
|
||||
return false, 0
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
// +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 ssh
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"github.com/gliderlabs/ssh"
|
||||
)
|
||||
|
||||
func listen(server *ssh.Server) {
|
||||
err := server.ListenAndServe()
|
||||
if err != nil {
|
||||
log.Critical("Failed to serve with builtin SSH server. %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Unused does nothing on windows
|
||||
func Unused() {
|
||||
// Do nothing
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package debug
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Log interface allows different log implementations to be used.
|
||||
type Log interface {
|
||||
Close() error
|
||||
Info(eid uint32, msg string) error
|
||||
Warning(eid uint32, msg string) error
|
||||
Error(eid uint32, msg string) error
|
||||
}
|
||||
|
||||
// ConsoleLog provides access to the console.
|
||||
type ConsoleLog struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// New creates new ConsoleLog.
|
||||
func New(source string) *ConsoleLog {
|
||||
return &ConsoleLog{Name: source}
|
||||
}
|
||||
|
||||
// Close closes console log l.
|
||||
func (l *ConsoleLog) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *ConsoleLog) report(kind string, eid uint32, msg string) error {
|
||||
s := l.Name + "." + kind + "(" + strconv.Itoa(int(eid)) + "): " + msg + "\n"
|
||||
_, err := os.Stdout.Write([]byte(s))
|
||||
return err
|
||||
}
|
||||
|
||||
// Info writes an information event msg with event id eid to the console l.
|
||||
func (l *ConsoleLog) Info(eid uint32, msg string) error {
|
||||
return l.report("info", eid, msg)
|
||||
}
|
||||
|
||||
// Warning writes an warning event msg with event id eid to the console l.
|
||||
func (l *ConsoleLog) Warning(eid uint32, msg string) error {
|
||||
return l.report("warn", eid, msg)
|
||||
}
|
||||
|
||||
// Error writes an error event msg with event id eid to the console l.
|
||||
func (l *ConsoleLog) Error(eid uint32, msg string) error {
|
||||
return l.report("error", eid, msg)
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
// Package debug provides facilities to execute svc.Handler on console.
|
||||
//
|
||||
package debug
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows/svc"
|
||||
)
|
||||
|
||||
// Run executes service name by calling appropriate handler function.
|
||||
// The process is running on console, unlike real service. Use Ctrl+C to
|
||||
// send "Stop" command to your service.
|
||||
func Run(name string, handler svc.Handler) error {
|
||||
cmds := make(chan svc.ChangeRequest)
|
||||
changes := make(chan svc.Status)
|
||||
|
||||
sig := make(chan os.Signal)
|
||||
signal.Notify(sig)
|
||||
|
||||
go func() {
|
||||
status := svc.Status{State: svc.Stopped}
|
||||
for {
|
||||
select {
|
||||
case <-sig:
|
||||
cmds <- svc.ChangeRequest{Cmd: svc.Stop, CurrentStatus: status}
|
||||
case status = <-changes:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
_, errno := handler.Execute([]string{name}, cmds, changes)
|
||||
if errno != 0 {
|
||||
return syscall.Errno(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in new issue