mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	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_TIMEOUT
This commit is contained in:
		@@ -227,7 +227,8 @@ func runWeb(ctx *cli.Context) error {
 | 
				
			|||||||
		log.Critical("Failed to start server: %v", err)
 | 
							log.Critical("Failed to start server: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	log.Info("HTTP Listener: %s Closed", listenAddr)
 | 
						log.Info("HTTP Listener: %s Closed", listenAddr)
 | 
				
			||||||
	graceful.WaitForServers()
 | 
						graceful.Manager.WaitForServers()
 | 
				
			||||||
 | 
						graceful.Manager.WaitForTerminate()
 | 
				
			||||||
	log.Close()
 | 
						log.Close()
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
// +build !windows
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Copyright 2016 The Gitea Authors. All rights reserved.
 | 
					// Copyright 2016 The Gitea Authors. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
@@ -27,11 +25,11 @@ func runHTTPSWithTLSConfig(listenAddr string, tlsConfig *tls.Config, m http.Hand
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
 | 
					// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
 | 
				
			||||||
func NoHTTPRedirector() {
 | 
					func NoHTTPRedirector() {
 | 
				
			||||||
	graceful.InformCleanup()
 | 
						graceful.Manager.InformCleanup()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NoMainListener tells our cleanup routine that we will not be using a possibly provided listener
 | 
					// NoMainListener tells our cleanup routine that we will not be using a possibly provided listener
 | 
				
			||||||
// for our main HTTP/HTTPS service
 | 
					// for our main HTTP/HTTPS service
 | 
				
			||||||
func NoMainListener() {
 | 
					func NoMainListener() {
 | 
				
			||||||
	graceful.InformCleanup()
 | 
						graceful.Manager.InformCleanup()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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() {
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -287,6 +287,9 @@ ALLOW_GRACEFUL_RESTARTS = true
 | 
				
			|||||||
; shutting down. Force shutdown if this process takes longer than this delay.
 | 
					; shutting down. Force shutdown if this process takes longer than this delay.
 | 
				
			||||||
; set to a negative value to disable
 | 
					; set to a negative value to disable
 | 
				
			||||||
GRACEFUL_HAMMER_TIME = 60s
 | 
					GRACEFUL_HAMMER_TIME = 60s
 | 
				
			||||||
 | 
					; Allows the setting of a startup timeout and waithint for Windows as SVC service
 | 
				
			||||||
 | 
					; 0 disables this.
 | 
				
			||||||
 | 
					STARTUP_TIMEOUT = 0
 | 
				
			||||||
; Static resources, includes resources on custom/, public/ and all uploaded avatars web browser cache time, default is 6h
 | 
					; Static resources, includes resources on custom/, public/ and all uploaded avatars web browser cache time, default is 6h
 | 
				
			||||||
STATIC_CACHE_TIME = 6h
 | 
					STATIC_CACHE_TIME = 6h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -189,6 +189,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 | 
				
			|||||||
- `LETSENCRYPT_EMAIL`: **email@example.com**: Email used by Letsencrypt to notify about problems with issued certificates. (No default)
 | 
					- `LETSENCRYPT_EMAIL`: **email@example.com**: Email used by Letsencrypt to notify about problems with issued certificates. (No default)
 | 
				
			||||||
- `ALLOW_GRACEFUL_RESTARTS`: **true**: Perform a graceful restart on SIGHUP
 | 
					- `ALLOW_GRACEFUL_RESTARTS`: **true**: Perform a graceful restart on SIGHUP
 | 
				
			||||||
- `GRACEFUL_HAMMER_TIME`: **60s**: After a restart the parent process will stop accepting new connections and will allow requests to finish before stopping. Shutdown will be forced if it takes longer than this time.
 | 
					- `GRACEFUL_HAMMER_TIME`: **60s**: After a restart the parent process will stop accepting new connections and will allow requests to finish before stopping. Shutdown will be forced if it takes longer than this time.
 | 
				
			||||||
 | 
					- `STARTUP_TIMEOUT`: **0**: Shutsdown the server if startup takes longer than the provided time. On Windows setting this sends a waithint to the SVC host to tell the SVC host startup may take some time. Please note startup is determined by the opening of the listeners - HTTP/HTTPS/SSH. Indexers may take longer to startup and can have their own timeouts.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Database (`database`)
 | 
					## Database (`database`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,7 +84,7 @@ func InitRepoIndexer() {
 | 
				
			|||||||
	if setting.Indexer.StartupTimeout > 0 {
 | 
						if setting.Indexer.StartupTimeout > 0 {
 | 
				
			||||||
		go func() {
 | 
							go func() {
 | 
				
			||||||
			timeout := setting.Indexer.StartupTimeout
 | 
								timeout := setting.Indexer.StartupTimeout
 | 
				
			||||||
			if graceful.IsChild && setting.GracefulHammerTime > 0 {
 | 
								if graceful.Manager.IsChild() && setting.GracefulHammerTime > 0 {
 | 
				
			||||||
				timeout += setting.GracefulHammerTime
 | 
									timeout += setting.GracefulHammerTime
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			select {
 | 
								select {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										187
									
								
								modules/graceful/manager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								modules/graceful/manager.go
									
									
									
									
									
										Normal file
									
								
							@@ -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()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										141
									
								
								modules/graceful/manager_unix.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								modules/graceful/manager_unix.go
									
									
									
									
									
										Normal file
									
								
							@@ -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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										162
									
								
								modules/graceful/manager_windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								modules/graceful/manager_windows.go
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -100,7 +100,7 @@ func CloseProvidedListeners() error {
 | 
				
			|||||||
// creates a new one using net.Listen.
 | 
					// creates a new one using net.Listen.
 | 
				
			||||||
func GetListener(network, address string) (net.Listener, error) {
 | 
					func GetListener(network, address string) (net.Listener, error) {
 | 
				
			||||||
	// Add a deferral to say that we've tried to grab a listener
 | 
						// Add a deferral to say that we've tried to grab a listener
 | 
				
			||||||
	defer InformCleanup()
 | 
						defer Manager.InformCleanup()
 | 
				
			||||||
	switch network {
 | 
						switch network {
 | 
				
			||||||
	case "tcp", "tcp4", "tcp6":
 | 
						case "tcp", "tcp4", "tcp6":
 | 
				
			||||||
		tcpAddr, err := net.ResolveTCPAddr(network, address)
 | 
							tcpAddr, err := net.ResolveTCPAddr(network, address)
 | 
				
			||||||
							
								
								
									
										19
									
								
								modules/graceful/net_windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								modules/graceful/net_windows.go
									
									
									
									
									
										Normal file
									
								
							@@ -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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -21,7 +21,7 @@ var killParent sync.Once
 | 
				
			|||||||
// KillParent sends the kill signal to the parent process if we are a child
 | 
					// KillParent sends the kill signal to the parent process if we are a child
 | 
				
			||||||
func KillParent() {
 | 
					func KillParent() {
 | 
				
			||||||
	killParent.Do(func() {
 | 
						killParent.Do(func() {
 | 
				
			||||||
		if IsChild {
 | 
							if Manager.IsChild() {
 | 
				
			||||||
			ppid := syscall.Getppid()
 | 
								ppid := syscall.Getppid()
 | 
				
			||||||
			if ppid > 1 {
 | 
								if ppid > 1 {
 | 
				
			||||||
				_ = syscall.Kill(ppid, syscall.SIGTERM)
 | 
									_ = syscall.Kill(ppid, syscall.SIGTERM)
 | 
				
			||||||
@@ -79,7 +79,3 @@ func RestartProcess() (int, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return process.Pid, nil
 | 
						return process.Pid, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
type filer interface {
 | 
					 | 
				
			||||||
	File() (*os.File, error)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
// +build !windows
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
					// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
@@ -19,37 +17,16 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type state uint8
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	stateInit state = iota
 | 
					 | 
				
			||||||
	stateRunning
 | 
					 | 
				
			||||||
	stateShuttingDown
 | 
					 | 
				
			||||||
	stateTerminate
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	// RWMutex for when adding servers or shutting down
 | 
					 | 
				
			||||||
	runningServerReg sync.RWMutex
 | 
					 | 
				
			||||||
	runningServerWG  sync.WaitGroup
 | 
					 | 
				
			||||||
	// ensure we only fork once
 | 
					 | 
				
			||||||
	runningServersForked bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// DefaultReadTimeOut default read timeout
 | 
						// DefaultReadTimeOut default read timeout
 | 
				
			||||||
	DefaultReadTimeOut time.Duration
 | 
						DefaultReadTimeOut time.Duration
 | 
				
			||||||
	// DefaultWriteTimeOut default write timeout
 | 
						// DefaultWriteTimeOut default write timeout
 | 
				
			||||||
	DefaultWriteTimeOut time.Duration
 | 
						DefaultWriteTimeOut time.Duration
 | 
				
			||||||
	// DefaultMaxHeaderBytes default max header bytes
 | 
						// DefaultMaxHeaderBytes default max header bytes
 | 
				
			||||||
	DefaultMaxHeaderBytes int
 | 
						DefaultMaxHeaderBytes int
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// IsChild reports if we are a fork iff LISTEN_FDS is set and our parent PID is not 1
 | 
					 | 
				
			||||||
	IsChild = len(os.Getenv(listenFDs)) > 0 && os.Getppid() > 1
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	runningServerReg = sync.RWMutex{}
 | 
					 | 
				
			||||||
	runningServerWG = sync.WaitGroup{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DefaultMaxHeaderBytes = 0 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB)
 | 
						DefaultMaxHeaderBytes = 0 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -61,36 +38,22 @@ type Server struct {
 | 
				
			|||||||
	network     string
 | 
						network     string
 | 
				
			||||||
	address     string
 | 
						address     string
 | 
				
			||||||
	listener    net.Listener
 | 
						listener    net.Listener
 | 
				
			||||||
	PreSignalHooks  map[os.Signal][]func()
 | 
					 | 
				
			||||||
	PostSignalHooks map[os.Signal][]func()
 | 
					 | 
				
			||||||
	wg          sync.WaitGroup
 | 
						wg          sync.WaitGroup
 | 
				
			||||||
	sigChan         chan os.Signal
 | 
					 | 
				
			||||||
	state       state
 | 
						state       state
 | 
				
			||||||
	lock        *sync.RWMutex
 | 
						lock        *sync.RWMutex
 | 
				
			||||||
	BeforeBegin func(network, address string)
 | 
						BeforeBegin func(network, address string)
 | 
				
			||||||
	OnShutdown  func()
 | 
						OnShutdown  func()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WaitForServers waits for all running servers to finish
 | 
					 | 
				
			||||||
func WaitForServers() {
 | 
					 | 
				
			||||||
	runningServerWG.Wait()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewServer creates a server on network at provided address
 | 
					// NewServer creates a server on network at provided address
 | 
				
			||||||
func NewServer(network, address string) *Server {
 | 
					func NewServer(network, address string) *Server {
 | 
				
			||||||
	runningServerReg.Lock()
 | 
						if Manager.IsChild() {
 | 
				
			||||||
	defer runningServerReg.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if IsChild {
 | 
					 | 
				
			||||||
		log.Info("Restarting new server: %s:%s on PID: %d", network, address, os.Getpid())
 | 
							log.Info("Restarting new server: %s:%s on PID: %d", network, address, os.Getpid())
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		log.Info("Starting new server: %s:%s on PID: %d", network, address, os.Getpid())
 | 
							log.Info("Starting new server: %s:%s on PID: %d", network, address, os.Getpid())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	srv := &Server{
 | 
						srv := &Server{
 | 
				
			||||||
		wg:      sync.WaitGroup{},
 | 
							wg:      sync.WaitGroup{},
 | 
				
			||||||
		sigChan:         make(chan os.Signal),
 | 
					 | 
				
			||||||
		PreSignalHooks:  map[os.Signal][]func(){},
 | 
					 | 
				
			||||||
		PostSignalHooks: map[os.Signal][]func(){},
 | 
					 | 
				
			||||||
		state:   stateInit,
 | 
							state:   stateInit,
 | 
				
			||||||
		lock:    &sync.RWMutex{},
 | 
							lock:    &sync.RWMutex{},
 | 
				
			||||||
		network: network,
 | 
							network: network,
 | 
				
			||||||
@@ -107,7 +70,7 @@ func NewServer(network, address string) *Server {
 | 
				
			|||||||
// ListenAndServe listens on the provided network address and then calls Serve
 | 
					// ListenAndServe listens on the provided network address and then calls Serve
 | 
				
			||||||
// to handle requests on incoming connections.
 | 
					// to handle requests on incoming connections.
 | 
				
			||||||
func (srv *Server) ListenAndServe(serve ServeFunction) error {
 | 
					func (srv *Server) ListenAndServe(serve ServeFunction) error {
 | 
				
			||||||
	go srv.handleSignals()
 | 
						go srv.awaitShutdown()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	l, err := GetListener(srv.network, srv.address)
 | 
						l, err := GetListener(srv.network, srv.address)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -117,8 +80,6 @@ func (srv *Server) ListenAndServe(serve ServeFunction) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	srv.listener = newWrappedListener(l, srv)
 | 
						srv.listener = newWrappedListener(l, srv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	KillParent()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	srv.BeforeBegin(srv.network, srv.address)
 | 
						srv.BeforeBegin(srv.network, srv.address)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return srv.Serve(serve)
 | 
						return srv.Serve(serve)
 | 
				
			||||||
@@ -150,7 +111,7 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string, serve ServeFuncti
 | 
				
			|||||||
// ListenAndServeTLSConfig listens on the provided network address and then calls
 | 
					// ListenAndServeTLSConfig listens on the provided network address and then calls
 | 
				
			||||||
// Serve to handle requests on incoming TLS connections.
 | 
					// Serve to handle requests on incoming TLS connections.
 | 
				
			||||||
func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFunction) error {
 | 
					func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFunction) error {
 | 
				
			||||||
	go srv.handleSignals()
 | 
						go srv.awaitShutdown()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	l, err := GetListener(srv.network, srv.address)
 | 
						l, err := GetListener(srv.network, srv.address)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -161,7 +122,6 @@ func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFun
 | 
				
			|||||||
	wl := newWrappedListener(l, srv)
 | 
						wl := newWrappedListener(l, srv)
 | 
				
			||||||
	srv.listener = tls.NewListener(wl, tlsConfig)
 | 
						srv.listener = tls.NewListener(wl, tlsConfig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	KillParent()
 | 
					 | 
				
			||||||
	srv.BeforeBegin(srv.network, srv.address)
 | 
						srv.BeforeBegin(srv.network, srv.address)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return srv.Serve(serve)
 | 
						return srv.Serve(serve)
 | 
				
			||||||
@@ -178,12 +138,12 @@ func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFun
 | 
				
			|||||||
func (srv *Server) Serve(serve ServeFunction) error {
 | 
					func (srv *Server) Serve(serve ServeFunction) error {
 | 
				
			||||||
	defer log.Debug("Serve() returning... (PID: %d)", syscall.Getpid())
 | 
						defer log.Debug("Serve() returning... (PID: %d)", syscall.Getpid())
 | 
				
			||||||
	srv.setState(stateRunning)
 | 
						srv.setState(stateRunning)
 | 
				
			||||||
	runningServerWG.Add(1)
 | 
						Manager.RegisterServer()
 | 
				
			||||||
	err := serve(srv.listener)
 | 
						err := serve(srv.listener)
 | 
				
			||||||
	log.Debug("Waiting for connections to finish... (PID: %d)", syscall.Getpid())
 | 
						log.Debug("Waiting for connections to finish... (PID: %d)", syscall.Getpid())
 | 
				
			||||||
	srv.wg.Wait()
 | 
						srv.wg.Wait()
 | 
				
			||||||
	srv.setState(stateTerminate)
 | 
						srv.setState(stateTerminate)
 | 
				
			||||||
	runningServerWG.Done()
 | 
						Manager.ServerDone()
 | 
				
			||||||
	// use of closed means that the listeners are closed - i.e. we should be shutting down - return nil
 | 
						// use of closed means that the listeners are closed - i.e. we should be shutting down - return nil
 | 
				
			||||||
	if err != nil && strings.Contains(err.Error(), "use of closed") {
 | 
						if err != nil && strings.Contains(err.Error(), "use of closed") {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -205,6 +165,10 @@ func (srv *Server) setState(st state) {
 | 
				
			|||||||
	srv.state = st
 | 
						srv.state = st
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type filer interface {
 | 
				
			||||||
 | 
						File() (*os.File, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type wrappedListener struct {
 | 
					type wrappedListener struct {
 | 
				
			||||||
	net.Listener
 | 
						net.Listener
 | 
				
			||||||
	stopped bool
 | 
						stopped bool
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
// +build !windows
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
					// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
@@ -7,29 +5,37 @@
 | 
				
			|||||||
package graceful
 | 
					package graceful
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// awaitShutdown waits for the shutdown signal from the Manager
 | 
				
			||||||
 | 
					func (srv *Server) awaitShutdown() {
 | 
				
			||||||
 | 
						select {
 | 
				
			||||||
 | 
						case <-Manager.IsShutdown():
 | 
				
			||||||
 | 
							// Shutdown
 | 
				
			||||||
 | 
							srv.doShutdown()
 | 
				
			||||||
 | 
						case <-Manager.IsHammer():
 | 
				
			||||||
 | 
							// Hammer
 | 
				
			||||||
 | 
							srv.doShutdown()
 | 
				
			||||||
 | 
							srv.doHammer()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						<-Manager.IsHammer()
 | 
				
			||||||
 | 
						srv.doHammer()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// shutdown closes the listener so that no new connections are accepted
 | 
					// shutdown closes the listener so that no new connections are accepted
 | 
				
			||||||
// and starts a goroutine that will hammer (stop all running requests) the server
 | 
					// and starts a goroutine that will hammer (stop all running requests) the server
 | 
				
			||||||
// after setting.GracefulHammerTime.
 | 
					// after setting.GracefulHammerTime.
 | 
				
			||||||
func (srv *Server) shutdown() {
 | 
					func (srv *Server) doShutdown() {
 | 
				
			||||||
	// only shutdown if we're running.
 | 
						// only shutdown if we're running.
 | 
				
			||||||
	if srv.getState() != stateRunning {
 | 
						if srv.getState() != stateRunning {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	srv.setState(stateShuttingDown)
 | 
						srv.setState(stateShuttingDown)
 | 
				
			||||||
	if setting.GracefulHammerTime >= 0 {
 | 
					 | 
				
			||||||
		go srv.hammerTime(setting.GracefulHammerTime)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if srv.OnShutdown != nil {
 | 
						if srv.OnShutdown != nil {
 | 
				
			||||||
		srv.OnShutdown()
 | 
							srv.OnShutdown()
 | 
				
			||||||
@@ -42,14 +48,7 @@ func (srv *Server) shutdown() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// hammerTime forces the server to shutdown in a given timeout - whether it
 | 
					func (srv *Server) doHammer() {
 | 
				
			||||||
// finished outstanding requests or not. if Read/WriteTimeout are not set or the
 | 
					 | 
				
			||||||
// max header size is very big a connection could hang...
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// srv.Serve() will not return until all connections are served. this will
 | 
					 | 
				
			||||||
// unblock the srv.wg.Wait() in Serve() thus causing ListenAndServe* functions to
 | 
					 | 
				
			||||||
// return.
 | 
					 | 
				
			||||||
func (srv *Server) hammerTime(d time.Duration) {
 | 
					 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		// We call srv.wg.Done() until it panics.
 | 
							// We call srv.wg.Done() until it panics.
 | 
				
			||||||
		// This happens if we call Done() when the WaitGroup counter is already at 0
 | 
							// This happens if we call Done() when the WaitGroup counter is already at 0
 | 
				
			||||||
@@ -62,7 +61,6 @@ func (srv *Server) hammerTime(d time.Duration) {
 | 
				
			|||||||
	if srv.getState() != stateShuttingDown {
 | 
						if srv.getState() != stateShuttingDown {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	time.Sleep(d)
 | 
					 | 
				
			||||||
	log.Warn("Forcefully shutting down parent")
 | 
						log.Warn("Forcefully shutting down parent")
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		if srv.getState() == stateTerminate {
 | 
							if srv.getState() == stateTerminate {
 | 
				
			||||||
@@ -74,48 +72,3 @@ func (srv *Server) hammerTime(d time.Duration) {
 | 
				
			|||||||
		runtime.Gosched()
 | 
							runtime.Gosched()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func (srv *Server) fork() error {
 | 
					 | 
				
			||||||
	runningServerReg.Lock()
 | 
					 | 
				
			||||||
	defer runningServerReg.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// only one server instance should fork!
 | 
					 | 
				
			||||||
	if runningServersForked {
 | 
					 | 
				
			||||||
		return errors.New("another process already forked. Ignoring this one")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	runningServersForked = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// We need to move the file logs to append pids
 | 
					 | 
				
			||||||
	setting.RestartLogsWithPIDSuffix()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, err := RestartProcess()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RegisterPreSignalHook registers a function to be run before the signal handler for
 | 
					 | 
				
			||||||
// a given signal. These are not mutex locked and should therefore be only called before Serve.
 | 
					 | 
				
			||||||
func (srv *Server) RegisterPreSignalHook(sig os.Signal, f func()) (err error) {
 | 
					 | 
				
			||||||
	for _, s := range hookableSignals {
 | 
					 | 
				
			||||||
		if s == sig {
 | 
					 | 
				
			||||||
			srv.PreSignalHooks[sig] = append(srv.PreSignalHooks[sig], f)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	err = fmt.Errorf("Signal %v is not supported", sig)
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RegisterPostSignalHook registers a function to be run after the signal handler for
 | 
					 | 
				
			||||||
// a given signal. These are not mutex locked and should therefore be only called before Serve.
 | 
					 | 
				
			||||||
func (srv *Server) RegisterPostSignalHook(sig os.Signal, f func()) (err error) {
 | 
					 | 
				
			||||||
	for _, s := range hookableSignals {
 | 
					 | 
				
			||||||
		if s == sig {
 | 
					 | 
				
			||||||
			srv.PostSignalHooks[sig] = append(srv.PostSignalHooks[sig], f)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	err = fmt.Errorf("Signal %v is not supported", sig)
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
// +build !windows
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
					// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -172,7 +172,7 @@ func InitIssueIndexer(syncReindex bool) {
 | 
				
			|||||||
	} else if setting.Indexer.StartupTimeout > 0 {
 | 
						} else if setting.Indexer.StartupTimeout > 0 {
 | 
				
			||||||
		go func() {
 | 
							go func() {
 | 
				
			||||||
			timeout := setting.Indexer.StartupTimeout
 | 
								timeout := setting.Indexer.StartupTimeout
 | 
				
			||||||
			if graceful.IsChild && setting.GracefulHammerTime > 0 {
 | 
								if graceful.Manager.IsChild() && setting.GracefulHammerTime > 0 {
 | 
				
			||||||
				timeout += setting.GracefulHammerTime
 | 
									timeout += setting.GracefulHammerTime
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			select {
 | 
								select {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -24,7 +24,6 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/generate"
 | 
						"code.gitea.io/gitea/modules/generate"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	_ "code.gitea.io/gitea/modules/minwinsvc" // import minwinsvc for windows services
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/user"
 | 
						"code.gitea.io/gitea/modules/user"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	shellquote "github.com/kballard/go-shellquote"
 | 
						shellquote "github.com/kballard/go-shellquote"
 | 
				
			||||||
@@ -99,6 +98,7 @@ var (
 | 
				
			|||||||
	LetsEncryptEmail     string
 | 
						LetsEncryptEmail     string
 | 
				
			||||||
	GracefulRestartable  bool
 | 
						GracefulRestartable  bool
 | 
				
			||||||
	GracefulHammerTime   time.Duration
 | 
						GracefulHammerTime   time.Duration
 | 
				
			||||||
 | 
						StartupTimeout       time.Duration
 | 
				
			||||||
	StaticURLPrefix      string
 | 
						StaticURLPrefix      string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SSH = struct {
 | 
						SSH = struct {
 | 
				
			||||||
@@ -569,6 +569,7 @@ func NewContext() {
 | 
				
			|||||||
	HTTPPort = sec.Key("HTTP_PORT").MustString("3000")
 | 
						HTTPPort = sec.Key("HTTP_PORT").MustString("3000")
 | 
				
			||||||
	GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true)
 | 
						GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true)
 | 
				
			||||||
	GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second)
 | 
						GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second)
 | 
				
			||||||
 | 
						StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(0 * time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultAppURL := string(Protocol) + "://" + Domain
 | 
						defaultAppURL := string(Protocol) + "://" + Domain
 | 
				
			||||||
	if (Protocol == HTTP && HTTPPort != "80") || (Protocol == HTTPS && HTTPPort != "443") {
 | 
						if (Protocol == HTTP && HTTPPort != "80") || (Protocol == HTTPS && HTTPPort != "443") {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
// +build !windows
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
					// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
@@ -26,5 +24,5 @@ func listen(server *ssh.Server) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Unused informs our cleanup routine that we will not be using a ssh port
 | 
					// Unused informs our cleanup routine that we will not be using a ssh port
 | 
				
			||||||
func Unused() {
 | 
					func Unused() {
 | 
				
			||||||
	graceful.InformCleanup()
 | 
						graceful.Manager.InformCleanup()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										56
									
								
								vendor/golang.org/x/sys/windows/svc/debug/log.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								vendor/golang.org/x/sys/windows/svc/debug/log.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										45
									
								
								vendor/golang.org/x/sys/windows/svc/debug/service.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								vendor/golang.org/x/sys/windows/svc/debug/service.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							@@ -482,6 +482,7 @@ golang.org/x/sys/cpu
 | 
				
			|||||||
golang.org/x/sys/unix
 | 
					golang.org/x/sys/unix
 | 
				
			||||||
golang.org/x/sys/windows
 | 
					golang.org/x/sys/windows
 | 
				
			||||||
golang.org/x/sys/windows/svc
 | 
					golang.org/x/sys/windows/svc
 | 
				
			||||||
 | 
					golang.org/x/sys/windows/svc/debug
 | 
				
			||||||
# golang.org/x/text v0.3.2
 | 
					# golang.org/x/text v0.3.2
 | 
				
			||||||
golang.org/x/text/encoding
 | 
					golang.org/x/text/encoding
 | 
				
			||||||
golang.org/x/text/encoding/charmap
 | 
					golang.org/x/text/encoding/charmap
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user